[游戏开发][Unity]Assetbundle加载篇(1)热更前准备与下载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文件扔到服务器上去下载。

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

第一行是SVN版本号

第二行是AB包数量

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

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

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

我们项目封装了一下UnityWebRequest,叫WebDataRequest,你不想封装直接用UnityWebRequest即可,加载成功后,解析downloader.的二进制数据,解析代码在下面。

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);
}
/// 
/// 补丁清单文件
/// 
public class PatchManifest
{
    private bool _isParse = false;

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

    /// 
    /// 所有打包文件列表
    /// 
    public readonly Dictionary Elements = new Dictionary();

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

    /// 
    /// 解析数据
    /// 
    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);
            }
        }
    }

    /// 
    /// 解析数据
    /// 
    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
{
    /// 
    /// 文件名称
    /// 
    public string Name { private set; get; }

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

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

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

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

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

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

    /// 
    /// 每次更新都会先下载到Sandbox_Temp目录,防止下到一半重启导致逻辑不一致报错
    /// temp目录下的文件在重新进入更新流程时先校验md5看是否要跳过下载
    /// 
    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);
    }
}

你可能感兴趣的:(项目管理,unity,游戏开发)