[Desarrollo de juegos] [Unity] Capítulo de descarga de Assetbundle (1) Preparación antes de la actualización en caliente y descarga de la lista de paquetes AB

Tabla de contenido

Directorio del marco de empaquetado y carga de recursos.

texto

El proceso de actualización en caliente no es fijo y la máquina de estado escrita por cada persona también es diferente, pero ciertamente hay algunos pasos necesarios, como descargar la lista, comparar versiones, descargar el paquete AB y marcar la finalización de la descarga. Cada uno de mis próximos artículos es un paso importante para descargar el paquete AB y probablemente no se pueda omitir.

Compruebe si existe la ruta de la zona de pruebas

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
}

Compruebe si existe el directorio temporal de descarga

Tenga en cuenta que al descargar la actualización en caliente, el paquete AB primero se descarga en el directorio temporal y luego se copia en el directorio sandbox.

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
}

Después de confirmar que existen las rutas, comience a descargar


Como se mencionó anteriormente, la lista de paquetes AB generada se ve así: este archivo se genera en un archivo de bytes binarios y se envía al servidor para su descarga.

La primera línea es el número de versión SVN.

La segunda línea es el número de paquetes AB.

A partir de la tercera línea está la información del paquete de recursos, separada por el signo = para separar los datos válidos, respectivamente.

MD5.unity3d = Ruta del recurso = HashId de la ruta del recurso = Tamaño de KB del cuerpo del paquete = Número de versión SVN = Iniciar el modo de actualización en caliente

Cada fila de datos encapsula una clase PatchElement, el código se encuentra a continuación

Nuestro proyecto encapsula UnityWebRequest y lo llama WebDataRequest. Si no desea encapsularlo, simplemente use UnityWebRequest. El código WebDataRequest está en la parte posterior.

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);
}

La clase PatchManifest es una clase que se especializa en analizar listas de paquetes AB. Puede ver al mirar el código que el propósito final del método Parse es analizar cada línea de datos en la lista en PatchElement y luego agregarlo al diccionario y guárdelo para llamarlo cuando lo descargue.

/// <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 es una encapsulación de cada fila de datos en la lista de paquetes AB. El análisis de PatchManifest creará un diccionario de elementos en un bucle para guardar estos datos.

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);
    }
}

El siguiente es el paquete de datos de descarga, que es esencialmente 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;
    }
}

Supongo que te gusta

Origin blog.csdn.net/liuyongjie1992/article/details/131107267
Recomendado
Clasificación