[游戏开发][Unity]Assetbundle下载篇(1)热更前准备与下载AB包清单

目录

打包与资源加载框架目录

正文

热更流程都不是固定的,每个人写的状态机也有所差别,但是一些必要步骤肯定不可能少,例如下载清单,对比版本,下载AB包,标记下载完成。我接下来的每一篇文章都是下载AB包的重要步骤,大概率是不能省略的。

检查沙盒路径是否存在

public static string MakePersistentLoadPath(string path)
{
#if UNITY_EDITOR
        // 注意:为了方便调试查看,编辑器下把存储目录放到项目里
        string projectPath = Path.GetDirectoryName(Application.dataPath).Replace("\\","/");
        projectPath = GetRegularPath(projectPath);
        return StringFormat.Format("{0}/Sandbox/{1}", projectPath, path);
#else
        return StringFormat.Format("{0}/Sandbox/{1}", Application.persistentDataPath, path);
#endif
}

检查下载临时目录是否存在

请注意,热更时下载AB包先下载到临时目录,之后再拷贝到沙盒目录

public static string MakeDownloadTempPath(string path)
{
#if UNITY_EDITOR
    string projectPath = Path.GetDirectoryName(Application.dataPath).Replace("\\", "/");
    projectPath = GetRegularPath(projectPath);
    return StringFormat.Format("{0}/Sandbox_Temp/{1}", projectPath, path);
#else
    return StringFormat.Format("{0}/Sandbox_Temp/{1}", Application.persistentDataPath, path);
#endif
}

路径都确认存在后,开始下载


前文有介绍过,生成的AB包清单长这个样子,把这个文件生成二进制bytes文件扔到服务器上去下载。

第一行是SVN版本号

第二行是AB包数量

从第三行开始是资源包信息,以=号分割开有效数据,分别是

MD5.unity3d = 资源路径 = 资源路径的HashId = 包体KB大小 = SVN版本号 = 启动热更模式

每一行数据封装了一个PatchElement类,代码在下文中

我们项目封装了一下UnityWebRequest,叫WebDataRequest,你不想封装直接用UnityWebRequest即可,WebDataRequest代码在后面有。

private IEnumerator DownLoad()
{
    // 解析APP里的补丁清单
    string filePath = AssetPathHelper.MakeStreamingLoadPath(PatchDefine.InitManifestFileName);
    string url = AssetPathHelper.ConvertToWWWPath(filePath);
    using (WebDataRequest downloader = new WebDataRequest(url))
    {
        yield return downloader.DownLoad();
        if (downloader.States == EWebRequestStates.Success) 
        {
            PatchHelper.Log(ELogLevel.Log, "Parse app patch manifest.");
            ParseAppPatchManifest(downloader.GetData());
        }
        else
        {
            throw new System.Exception($"Fatal error : Failed download file : {url}");
        }
    }
}

// 解析补丁清单文件相关接口
public void ParseAppPatchManifest(byte[] data)
{
    if (AppPatchManifest != null)
        throw new Exception("Should never get here.");
    AppPatchManifest = new PatchManifest(true);
    AppPatchManifest.Parse(data);
}

PatchManifest类是一个专门解析AB包清单的类,看代码也能知道,Parse方法的最终目的,就是把清单的每一行数据解析成PatchElement然后加入到字典中存起来等着下载时调用

/// <summary>
/// 补丁清单文件
/// </summary>
public class PatchManifest
{
    private bool _isParse = false;

    /// <summary>
    /// 资源版本号
    /// </summary>
    public int DllVersion { private set; get; }
    public int ResVersion { private set; get; }
    private bool IsInit = false;

    /// <summary>
    /// 所有打包文件列表
    /// </summary>
    public readonly Dictionary<string, PatchElement> Elements = new Dictionary<string, PatchElement>();

    public PatchManifest(bool isInit = false)
    {
        IsInit = isInit;
    }

    /// <summary>
    /// 解析数据
    /// </summary>
    public void Parse(byte[] data)
    {
        using (var ms = new MemoryStream(data))
        {
            using(var br = new BinaryReader(ms))
            {
                Parse(br);
            }
        }
    }

    public void ParseFile(string filePath)
    {
        using (var fs = File.OpenRead(filePath))
        {
            using (var br = new BinaryReader(fs))
            {
                Parse(br);
            }
        }
    }

    /// <summary>
    /// 解析数据
    /// </summary>
    public void Parse(BinaryReader br)
    {
        if (br == null)
            throw new Exception("Fatal error : Param is null.");
        if (_isParse)
            throw new Exception("Fatal error : Package is already parse.");

        _isParse = true;

        // 读取版本号            
        DllVersion = br.ReadInt32();
        ResVersion = br.ReadInt32();

        GameVersion.PatchResDesc = ResVersion + "   dllVer:" + DllVersion;
        int fileCount = br.ReadInt32();
        // 读取所有Bundle的数据
        for(var i = 0; i < fileCount; i++)
        {
            var ele = PatchElement.Deserialize(br, IsInit);
            if (Elements.ContainsKey(ele.Name))
                throw new Exception($"Fatal error : has same pack file : {ele.Name}");
            Elements.Add(ele.Name, ele);
        }
    }
}

PatchElement是AB包清单每一行数据的封装,PatchManifest的Parse里会循环创建一个Elements字典保存这些数据。

public class PatchElement
{
    /// <summary>
    /// 文件名称
    /// </summary>
    public string Name { private set; get; }

    /// <summary>
    /// 文件MD5
    /// </summary>
    public string MD5 { private set; get; }

    /// <summary>
    /// 文件版本
    /// </summary>
    public int Version { private set; get; }

    /// <summary>
    /// 文件大小
    /// </summary>
    public long SizeKB { private set; get; }

    /// <summary>
    /// 构建类型
    /// buildin 在安装包中
    /// ingame  游戏中下载
    /// </summary>
    public string Tag { private set; get; }

    /// <summary>
    /// 是否是安装包内的Patch
    /// </summary>
    public bool IsInit { private set; get; }

    /// <summary>
    /// 下载文件的保存路径
    /// </summary>
    public string SavePath;

    /// <summary>
    /// 每次更新都会先下载到Sandbox_Temp目录,防止下到一半重启导致逻辑不一致报错
    /// temp目录下的文件在重新进入更新流程时先校验md5看是否要跳过下载
    /// </summary>
    public bool SkipDownload { get; set; }


    public PatchElement(string name, string md5, int version, long sizeKB, string tag, bool isInit = false)
    {
        Name = name;
        MD5 = md5;
        Version = version;
        SizeKB = sizeKB;
        Tag = tag;
        IsInit = isInit;
        SkipDownload = false;
    }

    public void Serialize(BinaryWriter bw)
    {
        bw.Write(Name);
        bw.Write(MD5);
        bw.Write(SizeKB);
        bw.Write(Version);
        if (IsInit)
            bw.Write(Tag);
    }

    public static PatchElement Deserialize(BinaryReader br, bool isInit = false)
    {
        var name = br.ReadString();
        var md5 = br.ReadString();
        var sizeKb = br.ReadInt64();
        var version = br.ReadInt32();
        var tag = EBundlePos.buildin.ToString();
        if (isInit)
            tag = br.ReadString();
        return new PatchElement(name, md5, version, sizeKb, tag, isInit);
    }
}

下面是下载数据封装,本质还是 UnityWebRequest

public class WebDataRequest : WebRequestBase, IDisposable
{
    public WebDataRequest(string url) : base(url)
    {
    }
    public override IEnumerator DownLoad()
    {
        // Check fatal
        if (States != EWebRequestStates.None)
            throw new Exception($"{nameof(WebDataRequest)} is downloading yet : {URL}");

        States = EWebRequestStates.Loading;

        // 下载文件
        CacheRequest = new UnityWebRequest(URL, UnityWebRequest.kHttpVerbGET);
        DownloadHandlerBuffer handler = new DownloadHandlerBuffer();
        CacheRequest.downloadHandler = handler;
        CacheRequest.disposeDownloadHandlerOnDispose = true;
        CacheRequest.timeout = Timeout;
        yield return CacheRequest.SendWebRequest();

        // Check error
        if (CacheRequest.isNetworkError || CacheRequest.isHttpError)
        {
            MotionLog.LogWarning($"Failed to download web data : {URL} Error : {CacheRequest.error}");
            States = EWebRequestStates.Fail;
        }
        else
        {
            States = EWebRequestStates.Success;
        }
    }

    public byte[] GetData()
    {
        if (States == EWebRequestStates.Success)
            return CacheRequest.downloadHandler.data;
        else
            return null;
    }
    public string GetText()
    {
        if (States == EWebRequestStates.Success)
            return CacheRequest.downloadHandler.text;
        else
            return null;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
本书对Unity 3D集成开发环境界面、脚本的编写和众多高级特效的实现进行了详细介绍,内容深入浅出,是一本适合不同需求、不同开发水平读者的技术宝典。 全书共分16章。第1章主要介绍了Unity 3D的诞生、特点、开发环境的搭建及运行机制;第2章对Unity 3D集成开发环境进行了详细介绍;第3章介绍了Unity 3D中脚本的编写;第4章主要对Unity 3D开发过程中经常使用的组件及对象进行了详细介绍;第5章介绍了Unity游戏开发中非常流行的第三方UI界面开发组件库—NGUI的基础知识;第6章介绍了Unity开发平台的完整的物理引擎体系;第7章介绍了Unity 3D中的着色器和着色器语言—ShaderLab;第8章介绍了天空盒、虚拟按钮与摇杆、声音、水特效、3D拾取、重力加速度传感器及雾特效等开发常用的技术;第9章介绍了Unity中经常使用的光影效果,主要括各种光源、光照烘焙、法线贴图、镜面特效、波动水面真实效果等技术;第10章介绍了Unity中模型的网格概念及新旧动画系统;第11章介绍了Unity自带的地形引擎、拖尾渲染及导航网格和寻路系统等知识;第12章介绍了AssetBundle新资源的使用;第13章介绍了Unity中的多线程技术与网络开发;第14章介绍了Unity 2D游戏开发工具;第15章介绍了Unity 3D提供的Profiler工具的使用方法,及断点调试的两种方式;第16章介绍了完整的大型3D游戏案例—指间足球。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Little丶Seven

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值