使用Adressable完成游戏更新-自定义Bundle打包方式

新建脚本,继承自BuildScriptPackedMode,添加特性菜单,生成资源,重写3个方法,把这3个方法复制过来,并修改源码方法为virtual。

Name方法,修改返回的名字,就是你Build看见的名字。

BuildDataImplementation 这里修改的主要是初始化BuildInData,创建Resources/Version目录,存放BuildInData序列化后内容,然后打进包里。

PostProcessBundles 这里修改的主要是,把Bundle生成的名字记录到BuildInData(为准确加载准备,Bundle名字为组名+hash,所以可以直接判断名字),把打出来的Bundle复制到Library(因为都设置为远程加载了),使其可以复制到StreamAssets目录打进包里。


using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor.AddressableAssets.Build;
using UnityEditor.AddressableAssets.Build.DataBuilders;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.Initialization;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.ResourceProviders;

[CreateAssetMenu(fileName = "MyBuildScriptPackedMode.asset", menuName = "Addressable Assets/Data Builders/MyBuildScriptPackedMode")]
public class MyBuildScriptPackedMode : BuildScriptPackedMode
{
    public override string Name
    {
        get { return "My Build"; }//自定义Build的名字
    }

    protected override TResult BuildDataImplementation<TResult>(AddressablesDataBuilderInput builderInput)
    {
        TResult result = default(TResult);

        var timer = new Stopwatch();
        timer.Start();
        InitializeBuildContext(builderInput, out AddressableAssetsBuildContext aaContext);

        using (m_Log.ScopedStep(LogLevel.Info, "ProcessAllGroups"))
        {
            var errorString = ProcessAllGroups(aaContext);
            if (!string.IsNullOrEmpty(errorString))
                result = AddressableAssetBuildResult.CreateResult<TResult>(null, 0, errorString);
        }

        /////START 初始化buildInData
        AssetBundleManager.GetInstance().buildInData = new BuildInBundleData();
        var targetDir = Path.Combine(Application.dataPath, "Resources");
        if (!Directory.Exists(targetDir))
            Directory.CreateDirectory(targetDir);

        targetDir = Path.Combine(targetDir, "Version");
        if (!Directory.Exists(targetDir))
            Directory.CreateDirectory(targetDir);

        var targetPath = Path.Combine(targetDir, "BuildInBundleName.bytes");
        if (File.Exists(targetPath))
        {
            File.Delete(targetPath);
        }
        /////END 初始化buildInData 


        if (result == null)
        {
            result = DoBuild<TResult>(builderInput, aaContext);
        }

        if (result != null)
            result.Duration = timer.Elapsed.TotalSeconds;
        
        ////START 序列化保存本次打包的内置包列表
        var BuildInJson = JsonUtility.ToJson(AssetBundleManager.GetInstance().buildInData);
        File.WriteAllText(targetPath, BuildInJson);
        ////END 序列化保存本次打包的内置包列表

        return result;
    }

    public override void PostProcessBundles(AddressableAssetGroup assetGroup, List<string> buildBundles, List<string> outputBundles, IBundleBuildResults buildResult, ResourceManagerRuntimeData runtimeData, List<ContentCatalogDataEntry> locations, FileRegistry registry, Dictionary<string, ContentCatalogDataEntry> primaryKeyToCatalogEntry, Dictionary<string, string> bundleRenameMap, List<Action> postCatalogUpdateCallbacks)
    {
        var schema = assetGroup.GetSchema<BundledAssetGroupSchema>();
        if (schema == null)
            return;

        var path = schema.BuildPath.GetValue(assetGroup.Settings);
        if (string.IsNullOrEmpty(path))
            return;

        for (int i = 0; i < buildBundles.Count; ++i)
        {
            if (primaryKeyToCatalogEntry.TryGetValue(buildBundles[i], out ContentCatalogDataEntry dataEntry))
            {
                var info = buildResult.BundleInfos[buildBundles[i]];
                var requestOptions = new AssetBundleRequestOptions
                {
                    Crc = schema.UseAssetBundleCrc ? info.Crc : 0,
                    UseCrcForCachedBundle = schema.UseAssetBundleCrcForCachedBundles,
                    UseUnityWebRequestForLocalBundles = schema.UseUnityWebRequestForLocalBundles,
                    Hash = schema.UseAssetBundleCache ? info.Hash.ToString() : "",
                    ChunkedTransfer = schema.ChunkedTransfer,
                    RedirectLimit = schema.RedirectLimit,
                    RetryCount = schema.RetryCount,
                    Timeout = schema.Timeout,
                    BundleName = Path.GetFileNameWithoutExtension(info.FileName),
                    BundleSize = GetFileSize(info.FileName),
                    ClearOtherCachedVersionsWhenLoaded = schema.AssetBundledCacheClearBehavior == BundledAssetGroupSchema.CacheClearBehavior.ClearWhenWhenNewVersionLoaded
                };
                dataEntry.Data = requestOptions;

                int extensionLength = Path.GetExtension(outputBundles[i]).Length;
                string[] deconstructedBundleName = outputBundles[i].Substring(0, outputBundles[i].Length - extensionLength).Split('_');
                string reconstructedBundleName = string.Join("_", deconstructedBundleName, 1, deconstructedBundleName.Length - 1) + ".bundle";

                outputBundles[i] = ConstructAssetBundleName(assetGroup, schema, info, reconstructedBundleName);
                dataEntry.InternalId = dataEntry.InternalId.Remove(dataEntry.InternalId.Length - buildBundles[i].Length) + outputBundles[i];
                dataEntry.Keys[0] = outputBundles[i];
                ReplaceDependencyKeys(buildBundles[i], outputBundles[i], locations);

                if (!m_BundleToInternalId.ContainsKey(buildBundles[i]))
                    m_BundleToInternalId.Add(buildBundles[i], dataEntry.InternalId);

                if (dataEntry.InternalId.StartsWith("http:\\"))
                    dataEntry.InternalId = dataEntry.InternalId.Replace("http:\\", "http://").Replace("\\", "/");
                if (dataEntry.InternalId.StartsWith("https:\\"))
                    dataEntry.InternalId = dataEntry.InternalId.Replace("https:\\", "https://").Replace("\\", "/");
            }
            else
            {
                UnityEngine.Debug.LogWarningFormat("Unable to find ContentCatalogDataEntry for bundle {0}.", outputBundles[i]);
            }

            UnityEngine.Debug.Log(outputBundles[i]);
            if (!AssetBundleManager.GetInstance().buildInData.BuildInBundleNames.Contains(outputBundles[i]))
            {
                //添加打包的Bundle记录,内置Bundle
                AssetBundleManager.GetInstance().buildInData.BuildInBundleNames.Add(outputBundles[i]);
            }

            var targetPath = Path.Combine(path, outputBundles[i]);
            var srcPath = Path.Combine(assetGroup.Settings.buildSettings.bundleBuildPath, buildBundles[i]);
            bundleRenameMap.Add(buildBundles[i], outputBundles[i]);
            CopyFileWithTimestampIfDifferent(srcPath, targetPath, m_Log);

            AddPostCatalogUpdatesInternal(assetGroup, postCatalogUpdateCallbacks, dataEntry, targetPath, registry);

            //复制到Library 打包到包里面
            string RuntimePath = UnityEngine.AddressableAssets.Addressables.RuntimePath;
            string destPath = Path.Combine(System.Environment.CurrentDirectory, RuntimePath, PlatformMappingService.GetPlatformPathSubFolder().ToString(), outputBundles[i]);
            if (!Directory.Exists(Path.GetDirectoryName(destPath)))
                Directory.CreateDirectory(Path.GetDirectoryName(destPath));
            if (!File.Exists(destPath))
            {
                File.Copy(targetPath, destPath);
            }

            registry.AddFile(targetPath);
        }
    }
}


设置应用自定义打包

image.png

image.png

image.png


对了,设置AddressableAssetSettings的PlayerVersionOverried为固定版本,打包出来的catalog就都为同一个,更新这个用来检测更新。


首页 我的博客
粤ICP备17103704号