赞
踩
目录
(1)Unity(我用的版本是2020.3.8)导入AssetBundle Browser
AB包可以存储绝大部分Unity资源但无法直接存储C#脚本,所以代码的热更新需要使用Lua或者存储编译后的DLL文件。
AB包不能重复进行加载,当AB包已经加载进内存后必须卸载后才能重新加载。
多个资源分布在不同的AB包可能会出现一个预制体的贴图等部分资源不在同一个包下,直接加载会出现部分资源丢失的情况,即AB包之间是存在依赖关系的,在加载当前AB包时需要一并加载其所依赖的包。
打包完成后,会自动生成一个主包(主包名称随平台不同而不同),主包的manifest下会存储有版本号、校验码(CRC)、所有其它包的相关信息(名称、依赖关系)。
从github下载插件,将下载后的安装包解压到Unity工程的Packages文件夹下,如果报错,删除Tests即可。
正确获取到并安装完插件后,通过 Windows/AssetBundle Browser下打开AB包管理面板 一共有三个面板。利用AssetBundleBrowser打包时,我们用的是Build面板,设置好之后点击[build]即可。
Configure面板 :能查看当前AB包及其内部资源的基本情况(大小,资源,依赖情况等)。
Build面板:负责AssetBundle打包的相关设置。
Inspect面板:用来查看已经打包后的AB包文件的一些详细情况(大小,资源路径等)。
通过代码打AB包,不需要设置prefab的AssetBundle名称。
- private static void BuildAssetBundles(string tempDir, string outputDir, BuildTarget target)
- {
- Debug.Log("AB outputDir:"+ outputDir);
- Directory.CreateDirectory(tempDir);
- Directory.CreateDirectory(outputDir);
-
- List<AssetBundleBuild> abs = new List<AssetBundleBuild>();
- {
- var prefabAssets = new List<string>();
- string testPrefab = $"{Application.dataPath}/Prefabs/Cube.prefab";
- prefabAssets.Add(testPrefab);
- AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
- abs.Add(new AssetBundleBuild
- {
- assetBundleName = "prefabs",
- assetNames = prefabAssets.Select(s => ToRelativeAssetPath(s)).ToArray(),
- });
- }
-
- BuildPipeline.BuildAssetBundles(outputDir, abs.ToArray(), BuildAssetBundleOptions.None, target);
- AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
- }
AssetBundle 的管理和使用相对复杂,需要处理依赖关系、版本控制、加载策略等问题。因此,很多开发者会选择使用如 YooAsset 这样的资源管理框架来简化 AssetBundle 的使用,自动处理这些复杂的任务。
直接修改Packages文件夹下的清单文件manifest.json:
- {
- "dependencies": {
- "com.tuyoogame.yooasset": "2.1.0",
- ......
- },
- "scopedRegistries": [
- {
- "name": "package.openupm.cn",
- "url": "https://package.openupm.cn",
- "scopes": [
- "com.tuyoogame.yooasset"
- ]
- }
- ]
- }
unity2020.3.8出现以下报错,报错代码注释掉即可,貌似DownloadHandlerAssetBundle下还没有 autoLoadAssetBundle这个变量,而且支持的版本这个值默认false。
Project窗体内右键 -> Create -> YooAsset -> Create YooAsset Setting可创建YooAssetSettings全局配置文件,该文件需放在Resources文件夹下:
Project窗体内右键 -> Create -> YooAsset -> Create AssetBundle Collector Setting可创建该文件,需要注意的是一个工程只能有一个AssetBundleCollectorSetting。
重点关注Enable Addressable及Pack Rule打包规则:
启用可寻址资源定位系统。启用后加载资源时可以不写全路径,只根据资源名称即可加载:
YooAssets.LoadSceneAsync("scene_home");
规则可以自定义扩展。下面是内置规则:
加密类列表。Build时选的加密类型要和加载资源时的解密类型一致,否则加载资源时会报错。比如Build时选择的加密方式为FileOffsetEncryption,则文件解密服务接口也需要为FileOffset类型:
- {
- ...
- var initParameters = new HostPlayModeParameters();//HostPlayModeParameters继承自 InitializeParameters
- initParameters.BuildinQueryServices = new GameQueryServices();//内置资源查询服务接口
- initParameters.DecryptionServices = new FileOffsetDecryption();//如果资源包在构建的时候有加密,需要提供实现IDecryptionServices接口的实例类。
- ...
- }
- /// <summary>
- /// 资源文件偏移加载解密类
- /// </summary>
- private class FileOffsetDecryption : IDecryptionServices
- {
- /// <summary>
- /// 同步方式获取解密的资源包对象
- /// 注意:加载流对象在资源包对象释放的时候会自动释放
- /// </summary>
- AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream)
- {
- managedStream = null;
- return AssetBundle.LoadFromFile(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset());
- }
-
- /// <summary>
- /// 异步方式获取解密的资源包对象
- /// 注意:加载流对象在资源包对象释放的时候会自动释放
- /// </summary>
- AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream)
- {
- managedStream = null;
- return AssetBundle.LoadFromFileAsync(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset());
- }
-
- private static ulong GetFileOffset()
- {
- return 32;
- }
-
- }
以官方提供的Space Shooter这个Demo为例,重新编写YooAsset相关的流程。先确保Demo能正常运行:
打开Boot.cs文件,注释掉 IEnumerator Start方法,在TestLoad方法中一步一步进行YooAsset资源的加载:
- private void Start()
- {
- StartCoroutine(TestLoad());
- }
- IEnumerator TestLoad()
- {
- ...
- }
- IEnumerator TestLoad()
- {
- // 初始化资源系统
- YooAssets.Initialize();
-
- // 创建默认的资源包
- var package = YooAssets.CreatePackage("DefaultPackage");
-
- // 设置该资源包为默认的资源包,可以使用YooAssets相关加载接口加载该资源包内容。
- YooAssets.SetDefaultPackage(package);
- ...
- }
- ...
- if (PlayMode == EPlayMode.EditorSimulateMode)
- {
- var initParameters = new EditorSimulateModeParameters();//EditorSimulateModeParameters继承自InitializeParameters
- string simulateManifestFilePath = EditorSimulateModeHelper.SimulateBuild(EDefaultBuildPipeline.BuiltinBuildPipeline.ToString(), "DefaultPackage");
- initParameters.SimulateManifestFilePath = simulateManifestFilePath;
- yield return package.InitializeAsync(initParameters);
- }
- else if (PlayMode == EPlayMode.OfflinePlayMode)
- {
- var initParameters = new OfflinePlayModeParameters();//OfflinePlayModeParameters继承自InitializeParameters
- initParameters.DecryptionServices = new FileOffsetDecryption();//需要补充这个
- yield return package.InitializeAsync(initParameters);
- }
- else if (PlayMode == EPlayMode.HostPlayMode)
- {
- // 注意:GameQueryServices.cs 太空战机的脚本类,详细见StreamingAssetsHelper.cs
- string defaultHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";
- string fallbackHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";
- var initParameters = new HostPlayModeParameters();//HostPlayModeParameters继承自InitializeParameters
- initParameters.BuildinQueryServices = new GameQueryServices();//内置资源查询服务接口
- initParameters.DecryptionServices = new FileOffsetDecryption();//如果资源包在构建的时候有加密,需要提供实现IDecryptionServices接口的实例类。
- initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);//远端服务器查询服务接口
- var initOperation = package.InitializeAsync(initParameters);
- yield return initOperation;
-
- if (initOperation.Status == EOperationStatus.Succeed)
- {
- Debug.Log("资源包初始化成功!");
- }
- else
- {
- Debug.LogError($"资源包初始化失败:{initOperation.Error}");
- }
- }
- else if (PlayMode == EPlayMode.WebPlayMode)
- {
- string defaultHostServer = "http://127.0.0.1/CDN/WebGL/V1.0";
- string fallbackHostServer = "http://127.0.0.1/CDN/WebGL/V1.0";
- var initParameters = new WebPlayModeParameters();//WebPlayModeParameters继承自InitializeParameters
- initParameters.BuildinQueryServices = new GameQueryServices();
- initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
- var initOperation = package.InitializeAsync(initParameters);
- yield return initOperation;
-
- if (initOperation.Status == EOperationStatus.Succeed)
- {
- Debug.Log("资源包初始化成功!");
- }
- else
- {
- Debug.LogError($"资源包初始化失败:{initOperation.Error}");
- }
- }
补充两个类:
- /// <summary>
- /// 资源文件偏移加载解密类
- /// </summary>
- private class FileOffsetDecryption : IDecryptionServices
- {
- /// <summary>
- /// 同步方式获取解密的资源包对象
- /// 注意:加载流对象在资源包对象释放的时候会自动释放
- /// </summary>
- AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream)
- {
- managedStream = null;
- return AssetBundle.LoadFromFile(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset());
- }
-
- /// <summary>
- /// 异步方式获取解密的资源包对象
- /// 注意:加载流对象在资源包对象释放的时候会自动释放
- /// </summary>
- AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream)
- {
- managedStream = null;
- return AssetBundle.LoadFromFileAsync(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset());
- }
-
- private static ulong GetFileOffset()
- {
- return 32;
- }
-
- }
-
-
- /// <summary>
- /// 远端资源地址查询服务类
- /// </summary>
- private class RemoteServices : IRemoteServices
- {
- private readonly string _defaultHostServer;
- private readonly string _fallbackHostServer;
-
- public RemoteServices(string defaultHostServer, string fallbackHostServer)
- {
- _defaultHostServer = defaultHostServer;
- _fallbackHostServer = fallbackHostServer;
- }
- string IRemoteServices.GetRemoteMainURL(string fileName)
- {
- return $"{_defaultHostServer}/{fileName}";
- }
- string IRemoteServices.GetRemoteFallbackURL(string fileName)
- {
- return $"{_fallbackHostServer}/{fileName}";
- }
- }
- ...
- var package = YooAssets.GetPackage("DefaultPackage");
- var operation = package.UpdatePackageVersionAsync();
- yield return operation;
-
- if (operation.Status == EOperationStatus.Succeed)
- {
- //更新成功
- string packageVersion = operation.PackageVersion;
- Debug.Log($"Updated package Version : {packageVersion}");
- }
- else
- {
- //更新失败
- Debug.LogError(operation.Error);
- }
对于联机运行模式,在获取到资源版本号之后,就可以利用UpdatePackageManifestAsync更新资源清单了:
- ...
- bool savePackageVersion = true;
- var package = YooAssets.GetPackage("DefaultPackage");
- var operation = package.UpdatePackageManifestAsync(packageVersion, savePackageVersion);
- yield return operation;
-
- if (operation.Status == EOperationStatus.Succeed)
- {
- //更新成功
- }
- else
- {
- //更新失败
- Debug.LogError(operation.Error);
- }
- IEnumerator Download()
- {
- int downloadingMaxNum = 10;
- int failedTryAgain = 3;
- var package = YooAssets.GetPackage("DefaultPackage");
-
- //创建资源下载器,下载所有资源
- var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain);
-
- //没有需要下载的资源
- if (downloader.TotalDownloadCount == 0)
- {
- Debug.Log("TotalDownloadCount = 0");
-
- yield break;
- }
-
- //需要下载的文件总数和总大小
- int totalDownloadCount = downloader.TotalDownloadCount;
- long totalDownloadBytes = downloader.TotalDownloadBytes;
-
- //注册回调方法
- downloader.OnDownloadErrorCallback = OnDownloadErrorFunction;
- downloader.OnDownloadProgressCallback = OnDownloadProgressUpdateFunction;
- downloader.OnDownloadOverCallback = OnDownloadOverFunction;
- downloader.OnStartDownloadFileCallback = OnStartDownloadFileFunction;
-
- //开启下载
- downloader.BeginDownload();
- yield return downloader;
-
- //检测下载结果
- if (downloader.Status == EOperationStatus.Succeed)
- {
- Debug.Log("Finish");
- }
- else
- {
- Debug.Log("DownLoad Failed");
- }
- }
-
- private void OnStartDownloadFileFunction(string fileName, long sizeBytes)
- {
- Debug.Log("fileName:" + fileName + ",sizeBytes:" + sizeBytes);
- }
-
- private void OnDownloadOverFunction(bool isSucceed)
- {
- Debug.Log("isSucceed");
- }
-
- private void OnDownloadProgressUpdateFunction(int totalDownloadCount, int currentDownloadCount, long totalDownloadBytes, long currentDownloadBytes)
- {
- Debug.Log("totalDownloadCount:" + totalDownloadCount + ",currentDownloadCount" + currentDownloadCount + ",totalDownloadBytes:" + totalDownloadBytes + ",currentDownloadBytes" + currentDownloadBytes);
- }
-
- private void OnDownloadErrorFunction(string fileName, string error)
- {
- Debug.Log("DownloadError:" + fileName + ",error:" + error);
- }
- //加载场景,启用可寻址功能(Enable Addressable)后,不用写全路径,直接写资源名称即可
- string location = "scene_home";
- var sceneMode = UnityEngine.SceneManagement.LoadSceneMode.Single;
- bool suspendLoad = false;
- SceneHandle handle = package.LoadSceneAsync(location, sceneMode, suspendLoad);
- yield return handle;
- Debug.Log($"Scene name is {handle.SceneObject.name}");
-
- //加载预制体
- AssetHandle handle0 = package.LoadAssetAsync<GameObject>("UIHome");//不用加后缀
- yield return handle0;
- GameObject go = handle0.InstantiateSync();
- Debug.Log($"Prefab name is {go.name}");
-
- //加载音频
- AssetHandle handle1 = package.LoadAssetAsync<AudioClip>("music_background");
- yield return handle1;
- AudioClip audioClip = handle1.AssetObject as AudioClip;
- GameObject audio = new GameObject("AudioSource");
- audio.AddComponent<AudioSource>().clip= audioClip;
- audio.GetComponent<AudioSource>().Play();
- IEnumerator TestLoad()
- {
- //0.别忘初始化项目中这两个事件相关的类
- // 游戏管理器 注册场景事件
- GameManager.Instance.Behaviour = this;
- // 初始化事件系统
- UniEvent.Initalize();
-
-
- //1.初始化
- Debug.Log("1. 初始化");
- // 初始化资源系统
- YooAssets.Initialize();
-
- // 创建默认的资源包
- var package = YooAssets.CreatePackage("DefaultPackage");
-
- // 设置该资源包为默认的资源包,可以使用YooAssets相关加载接口加载该资源包内容。
- YooAssets.SetDefaultPackage(package);
-
- //2.资源系统的运行模式
- Debug.Log("2. 资源系统的运行模式");
- if (PlayMode == EPlayMode.EditorSimulateMode)
- {
- var initParameters = new EditorSimulateModeParameters();//EditorSimulateModeParameters继承自InitializeParameters
- string simulateManifestFilePath = EditorSimulateModeHelper.SimulateBuild(EDefaultBuildPipeline.BuiltinBuildPipeline.ToString(), "DefaultPackage");
- initParameters.SimulateManifestFilePath = simulateManifestFilePath;
- yield return package.InitializeAsync(initParameters);
- }
- else if (PlayMode == EPlayMode.OfflinePlayMode)
- {
- var initParameters = new OfflinePlayModeParameters();//OfflinePlayModeParameters继承自InitializeParameters
- initParameters.DecryptionServices = new FileOffsetDecryption();//需要补充这个
- yield return package.InitializeAsync(initParameters);
- }
- else if (PlayMode == EPlayMode.HostPlayMode)
- {
- // 注意:GameQueryServices.cs 太空战机的脚本类,详细见StreamingAssetsHelper.cs
- string defaultHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";
- string fallbackHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";
- var initParameters = new HostPlayModeParameters();//HostPlayModeParameters继承自InitializeParameters
- initParameters.BuildinQueryServices = new GameQueryServices();//内置资源查询服务接口
- initParameters.DecryptionServices = new FileOffsetDecryption();//如果资源包在构建的时候有加密,需要提供实现IDecryptionServices接口的实例类。
- initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);//远端服务器查询服务接口
- var initOperation = package.InitializeAsync(initParameters);
- yield return initOperation;
-
- if (initOperation.Status == EOperationStatus.Succeed)
- {
- Debug.Log("资源包初始化成功!");
- }
- else
- {
- Debug.LogError($"资源包初始化失败:{initOperation.Error}");
- }
- }
- else if (PlayMode == EPlayMode.WebPlayMode)
- {
- string defaultHostServer = "http://127.0.0.1/CDN/WebGL/V1.0";
- string fallbackHostServer = "http://127.0.0.1/CDN/WebGL/V1.0";
- var initParameters = new WebPlayModeParameters();//WebPlayModeParameters继承自InitializeParameters
- initParameters.BuildinQueryServices = new GameQueryServices();
- initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
- var initOperation = package.InitializeAsync(initParameters);
- yield return initOperation;
-
- if (initOperation.Status == EOperationStatus.Succeed)
- {
- Debug.Log("资源包初始化成功!");
- }
- else
- {
- Debug.LogError($"资源包初始化失败:{initOperation.Error}");
- }
- }
-
- //3.获取资源版本:UpdatePackageVersionAsync
- Debug.Log("3. 获取资源版本");
- var operation = package.UpdatePackageVersionAsync();
- yield return operation;
-
- if (operation.Status == EOperationStatus.Succeed)
- {
-
- string packageVersion = operation.PackageVersion;
- Debug.Log($"Updated package Version : {packageVersion}");
-
- //4.更新资源清单:对于联机运行模式,在获取到资源版本号之后,就可以更新资源清单了:UpdatePackageManifestAsync
- //联机运行模式
- //通过传入的清单版本,优先比对当前激活清单的版本,如果相同就直接返回成功。如果有差异就从缓存里去查找匹配的清单,如果缓存里不存在,就去远端下载并保存到沙盒里。最后加载沙盒内匹配的清单文件。
- Debug.Log("4. 更新资源清单");
- bool savePackageVersion = true;
- var operation2 = package.UpdatePackageManifestAsync(packageVersion, savePackageVersion);
- yield return operation2;
-
- if (operation2.Status == EOperationStatus.Succeed)
- {
- //5.资源包下载
- Debug.Log("5. 资源包下载");
- yield return Download();
- //6.加载场景,启用可寻址功能(Enable Addressable)后,不用写全路径,直接写资源名称即可
- //YooAssets.LoadSceneAsync("scene_home");
- string location = "scene_home";
- var sceneMode = UnityEngine.SceneManagement.LoadSceneMode.Single;
- bool suspendLoad = false;
- SceneHandle handle = package.LoadSceneAsync(location, sceneMode, suspendLoad);
- yield return handle;
- Debug.Log($"Scene name is {handle.SceneObject.name}");
- //7.加载预制体
- AssetHandle handle0 = package.LoadAssetAsync<GameObject>("UIHome");//不用加后缀
- yield return handle0;
- GameObject go = handle0.InstantiateSync();
- Debug.Log($"Prefab name is {go.name}");
- //8.加载音频
- AssetHandle handle1 = package.LoadAssetAsync<AudioClip>("music_background");
- yield return handle1;
- AudioClip audioClip = handle1.AssetObject as AudioClip;
- GameObject audio = new GameObject("AudioSource");
- audio.AddComponent<AudioSource>().clip= audioClip;
- audio.GetComponent<AudioSource>().Play();
-
- //9.资源释放
- handle0.Release();
- package.UnloadUnusedAssets();
- }
- else
- {
- //更新失败
- Debug.LogError(operation.Error);
- }
- }
- else
- {
- //更新失败
- Debug.LogError(operation.Error);
- }
-
- }
无论是通过增量构建还是强制构建,都会生成一个以Build Version命名的文件夹,我们把这个文件夹统称为补丁包。补丁包里包含了游戏运行需要的所有资源,我们可以无脑的将补丁包内容覆盖到CDN目录下。
Host Play Mode下,YooAsset资源加载顺序是:先检查StreamingAsset目录,再检查同Library目录的的Yoo缓存目录(_data),最后才Host服务器下载。
Offline Play Mode下:检查StreamingAsset目录。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。