Unity|Unity Addressables资源管理系统

【Unity|Unity Addressables资源管理系统】
文章目录

  • Addressable Asset System(可寻址资产系统)
    • 1. 背景
    • 2. Addressable系统介绍
    • 3. Addressable系统优势:
    • 4. Addressable系统与AssetBundle的区别
  • Addressable系统的使用
    • 1. 安装
    • 2. Addressables管理窗口
    • 3. AddressableAssetSettings系统设置
    • 4. 资源组设置
    • 5. 标记资源
    • 6. 资源打包
    • 7. 加载资源
    • 8. 更新资源包
    • 9. 热更新代码和方案
    • 10. 资源自动分组
    • 11. 内存管理
    • 12. Addressable打包粒度

Addressable Asset System(可寻址资产系统) 1. 背景 ??在开始介绍Addressable系统之前,我们先来回顾下传统的几种资源加载方式:
  • 直接引用: 使用直接引用是最简单快捷的,但不可动态加载。
  • Resources资源管理: Resources文件夹下所有文件都会打包到安装包,无论资源是否被使用,这样就会导致包体过大;Resources没办法做热更新资源;Resources加载资源时,对资源路径要求严格。
  • AssetBundle资源管理: 可热更,但管理难度大。
2. Addressable系统介绍 ??可寻址资产系统是Unity推出的新的资源管理插件。Addressable系统是在 Asset Bundle之上,提供了异步加载、依赖管理以及内存管理等丰富的资源管理功能,也能够让开发者实现远程资源更新更加的便捷。
3. Addressable系统优势:
  • 快速迭代:
    使用Addressable在开发前期就进入快速开发的阶段,无论使用任何你喜欢的资源管理技术,都能快速切换到Addressable系统中。几乎不需要修改代码。
  • 依赖管理:
    系统不仅返回请求内容,还返回该内容所有依赖项,以便在返回内容之前加载所有网格、着色器、动画等。
  • 内存管理:
    Addressable不仅仅能加载资源,同时也能卸载资源。
  • 内容打包:
    Addressable系统自动管理了所有复杂的依赖连接,所以即使资源移动了或是重新命名了,系统依然能够高效地找到准确的依赖进行打包。当你需要将打包的资源从本地移到服务器上面,Addressable系统也能轻松做到。 相较于传统的资产加载方式(Resources/AssetBundle),Addressable系统拥有了完备的可视化编辑窗口以及内存管理。
4. Addressable系统与AssetBundle的区别
  • Addressable系统只需要资产的地址就可以从任意位置加载,而AssetBundle需要从指定bundle包中加载资源。
  • Addressable系统使用引用计数自动管理内存卸载,而AssetBundle需要开发者手动管理。
  • Addressable系统自动管理依赖关系,而AssetBundle需要开发者自己管理依赖关系,维护起来比较困难。
  • 可寻址资源系统默认的所有加载操作都是异步操作,可以添加事件监听,而AssetBundle则有同步和异步加载。
    Unity|Unity Addressables资源管理系统
    文章图片
Addressable系统的使用 1. 安装 ????Unity 2018.2及以上版本,使用PackageManager安装Addressables。
Unity|Unity Addressables资源管理系统
文章图片

2. Addressables管理窗口 ????Window > Asset Management > Addressables > Groups进入使用界面
Unity|Unity Addressables资源管理系统
文章图片

Addressable设置:
????资源默认分为Built In Data和LocalGroup(Default)两组,前者包含一些内置资源,不能改动,后者可以进行添加或删除资源。
  • Create: 创建新的资源分组,也可以在Addressables Group窗口中右键 Create New Group > Packed Assets创建新的资源组。
  • Profile: Profile的作用主要是用于指定项目中需要用到的几个地址,我们可以在这个窗口中点击Create->Profile创建Profile,每个Profile中都包含4个默认的变量,我们也可以点击Create->Variable扩展变量。
  • Tools:
    • Profiles: 用于给整个Addressable设置范围(配置四种路径的具体值)之后可以给每个组的Build,Load选择路径种类,则根据Profile的选取确定。
    • Labels: 添加删除标签Label。
    • Analyze: Analyze是一个工具,收集有关您的项目的可寻址布局的信息。在某些情况下,Analyze可能会采取适当的措施来清理项目的状态。在其他情况下,Analyze纯粹是一种信息工具,使您可以对可寻址布局做出更明智的决策。Unity|Unity Addressables资源管理系统
      文章图片
    • Hosting Services: Addressables系统自带的一个资源服务,可以指定一个目录存放远程资源,然后通过连接这个服务器,来更新资源。
    • Event Viewer: 可以用于监测,调试。前提是在Inspect System Settings窗口勾选发送消息(由于消耗性能所以默认关闭)。
      ????使用Addressables事件查看器可监视所有Addressables系统操作的资源的引用计数。在Unity中选择Window > Asset Management > Event Viewer。
      ????请注意,事件查看器只关心引用计数,而不关心内存消耗。在"Asset"栏下列出每一帧中,可以看到每一个资源的如下信息:
      ????FPS: 每秒帧数。
      ????MonoHeap: 内存使用的总量。
      ????Event Counts: 每帧事件总数。
      Unity|Unity Addressables资源管理系统
      文章图片

      ????可以单击左箭头和右箭头逐帧观察,或者单击Current跳转到最新的帧。按+按钮展开一行以获得更多详细信息。
      ????事件查看器中显示的信息与在Play Mode Script中选择的游戏模式有关。 使用事件查看器时,应避免使用 Use Asset Database模式,因为它不考虑资产之间的任何依赖关系。使用Simulate Groups 或Use Existing Build模式,但是后者更适合于事件查看器,因为它可以更准确地监视资源的引用计数。
    • Check for Content Updata Restrictions: 更新静态资源组。
  • Play Mode Script:
    • Use Asset Database(fastest): 允许你在游戏流程中快速运行游戏。它直接通过Asset Database加载Asset ,这会在不需要分析器或assetBundle创建的情况下进行快速迭代。
    • Simulate Groups(advanced): 在不创建assetBundles的情况下分析布局和依赖项的内容。asset 通过ResourceManager从assetDataBase加载,就假装它们是通过包加载的一样。若要查看游戏期间bundles加载或卸载的时间,请在Addressables事件查看器窗口Tools > Event Viewer
    • Use Exiting Build(requires built groups): 最接近于已部署的应用程序生成,但它要求你将数据作为单独的步骤进行构建。如果不修改Asset,则此模式是最快的,因为它在进入Play模式时不处理任何数据。必须通过选择Build>New Build>Default Build Script,或者在游戏脚本中使用AddressableAssetSettings.BuildPlayerContent()方法,在Addressables组窗口(Window>Asset Management>Addressable>group>group)中构建此模式的内容。
  • Build:
    • New Build: 当所有资源都已准备就绪,点击此项打包。
    • Update a Previous Build: 资源更新时,点击此项更新资源包。
    • Clear Build: 清除已经Build的资源,再次运行游戏需要重新Build。
3. AddressableAssetSettings系统设置 Unity|Unity Addressables资源管理系统
文章图片

Addressable系统的基础配置:
  • Disable Catalog Update On Startup: 默认是没有勾选的,没有勾选,那么每次AA系统初始化的时候,会自动更新catalog文件,勾选上,将不会自动更新catalog文件,也就意味着不会自动更新资源.AA系统的初始化会在任意接口第一次调用时初始化,也可以主动调用Addressables.InitializeAsync()初始化.
  • Build Remote Catalog: 默认没有勾选,只有勾选上才会创建catalog在指定目录,以后客户端才可以下载这个catalog来进行对比更新.
  • Build Path: 资源打包后存放的地址。
  • Load Path: 资源加载地址。
  • Send Profiler Events: 调试用,允许加载资源的时候发送事件给EventViewer,可以通过这个工具查看资源的使用情况
  • Log Runtime Exception: 输出加载资源时的异常,开启时如果资源加载发生异常,会直接抛出.如果关闭,我们也可以通过加载资源时返回的句柄,来获取到异常信息.同时我们也可以给项目添加宏ADDRESSABLES_LOG_ALL,来查看更多的日志信息
4. 资源组设置 Unity|Unity Addressables资源管理系统
文章图片

  • Build Path: 资源打包后存放的地址。
  • Load Path: 资源加载地址。
  • Advanced Options(高级设置):
    • Asset Bundle Compression: 当前.bundle文件的压缩方式,支持LZ4和LZMA压缩。
    • Include in Build: 是否被打包,默认是勾选的,若不勾选当前组不打包。
    • Requset Timeout: 设置UnityWebRequest在超时的秒数超过后尝试中止。(仅适用于远程资源包)
    • Bundle Mode: 控制捆绑包的打包方式。PackTogether将一个分组打包成一个资源包;Pack Separately每一个资源打成一个包,Pack Together By Label将Lable相同资源打成一个资源包。
  • Content Update Restriction:
    • Update Restriciton:
      • Cannot Change Post Release: 静态Group,在发布后,无法被修改,只能通过Check for Content Updata Restrictions做增量更新,创建一个新的资源分组.
      • Can Change Post Release: 非静态Group,在发布后,允许修改,更新时做覆盖更新。
5. 标记资源 Unity|Unity Addressables资源管理系统
文章图片

????这里有两种方式将资源标记成可寻址的,在安装好可寻址资源包后,你可以在属性面板进行标记或者将其拖拽到管理窗口指定分组上。在资源的属性窗口上,点击Address复选框并为资源设置唯一标识符。
注:如果我们标记的资产在Resources文件夹下时,Addressable系统会提示你讲资产移出Resources文件夹。
Unity|Unity Addressables资源管理系统
文章图片

6. 资源打包 ????配置好资源分组,根据需要设置Play Mode Script,再通过Build > New Build > Default Build Script打包测试。
????本地资源打包路径: Library/com.unity.addressables/StreamingAssetsCopy/aa/Android/
????远程资源打包路径: ServerData/Android/
Unity|Unity Addressables资源管理系统
文章图片

????同时远程目录下会生成有.hash和.json文件,.hash文件内只包含一个catalog文件的Hash值,用于客户单检测catalog更新时,通过对比这个hash值,判断是否有catalog更新,json文件内包含每个Ab包的hash值和地址。
????使用Build > Update a previous Build 更新资源包时,需要选择一个bin文件(android环境为例,Anroid/.bin),这个bin文件记录了所有Ab包之间的依赖关系和分组信息,Addressable系统通过这个bin文件管理依赖。
7. 加载资源 ????使用AssetReference加载资源:
[SerializeField] private AssetReference m_AssetReference; privatevoid Start() { m_AssetReference.LoadAssetAsync(); }

????使用Addressables加载单个资源:
private void OnResLoadAsset(string key) { Addressables.LoadAssetAsync(key).Completed += OnCompleteLoad; } private void OnCompleteLoad(AsyncOperationHandle asyncOperationHandle) { GameObject go = GameObject.Instantiate(asyncOperationHandle.Result); } private void OnResInstantiate(string key) { Addressables.InstantiateAsync(key); }

???? 加载多个资源:
private void OnResLoadAsset(string key,string lable) { Addressables.LoadAssetsAsync(new List { key, lable }, null, Addressables.MergeMode.Intersection).Completed += OnCompleteLoadAssets; } private void OnCompleteLoadAssets(AsyncOperationHandle> asyncOperationHandle) { //DebugTools.Log(asyncOperationHandle.Result.Count); }
注:第三个参数,MergeMode查找资源的合并模式,以传入的参数是new List{key,label}为例
  • Node或UseFirst时,会取第一个key查询到的资源
  • Union时,取并集
  • Intersection时,取交集
小结:
(1)加载资源时,若加载资源指定的类型与资源类型不一致,Addressable系统找不到该资源,则抛出异常,无法加载资源,前提:系统设置勾选了Send Profiler Events。
(2)使用标签管理,同一个资源的地址和标签可以相同,当有多个资源标签相同,Addressable系统会返回第一个满足条件的资源。
(3)若资源的地址名称与下一个资源的标签相同,返回还是第一个资源,Addressable系统会对比资源的地址和标签,若都不相同,才会继续向下查找
8. 更新资源包 ????热更新资源包修改后,需要对资源重新打包
????Check for Content Update Restrictions: 针对是静态资源组,既是Update Restriciton属性为Cannot Change Post Release值。点击后弹出选择之前打包资源组生成的bin文件,点击“Apply Changes”应用更改,增加或修改的资源会被移动到新建Content Update分组。Unity|Unity Addressables资源管理系统
文章图片

????Update a Previous Build: 动态资源组更新时,执行该操作,同样需要选择bin文件,系统会自动生成一个新的AB包。
Unity|Unity Addressables资源管理系统
文章图片

9. 热更新代码和方案 ????核心代码
public IEnumerator CheckForContentUpdate(List keys) { for (int index = 0; index < keys.Count; index++) { AsyncOperationHandle DownloadSize = Addressables.GetDownloadSizeAsync(keys[index]); yield return DownloadSize; if (DownloadSize.Result <= 0) { Debug.Log("[Addressable]:不需要更新的资源标签:" + keys[index]); keys.Remove(keys[index]); } else { m_TotalSize += DownloadSize.Result / Mathf.Pow(1024, 2); } } m_DownloadDependencies = Addressables.DownloadDependenciesAsync(keys, Addressables.MergeMode.Union, false); yield return m_DownloadDependencies; }
  • 方案一:
    ????从服务器获取更新资源标签。
注:动态资源更新,旧资源会被覆盖,动态资源组中有一个资源需要更新,热更时会将整个资源组都下载下来
,因此合理划分资源分组十分重要,减少重复下载以及打包粒度(多个资源需要相同的材质、贴图等资源)。
  • 方案二:
    ????使用官方提供的Addressables.CheckForCatalogUpdates()方法检查目录,获取需要更新的目录。
  • 方案三:
    ????暴力获取所有Key,使用官方的Addressables.GetDownloadSizeAsync(Key)检查资源是否需要更新,从而获取需要更新的所有Key值。
    ????资源更新具体实现:
    ????????使用官方提供的Addressables.GetDownloadSizeAsync(Key)方法获取所有需要更新的资源地址或标签集合,再通过Addressables.DownloadDependenciesAsync(Key, Addressables.MergeMode.Union, false)下载更新的资源。
10. 资源自动分组 ????项目中需要管理的资源过多时,勾选Addressable或拖拽的方式明显不在合适,因此需要实现一个方法,将某个文件夹下的所有资源标记为可寻址资源。
????编辑状态创建一个新的菜单,并创建一个asset文件,配置需要标记的资源文件夹,可同时标记多个资源,配置如下:
Unity|Unity Addressables资源管理系统
文章图片

public static void AutoSetGroup(string groupName, string lableName, string assetPath, bool isSimplied = false) { var set = AddressableAssetSettingsDefaultObject.Settings; AddressableAssetGroup Group = set.FindGroup(groupName); if (Group == null) { Group = set.CreateGroup(groupName, false, false, false, new List { set.DefaultGroup.Schemas[0], set.DefaultGroup.Schemas[1]}, typeof(SchemaType)); } string Guid = AssetDatabase.AssetPathToGUID(assetPath); //获取指定路径下资源的 GUID(全局唯一标识符) AddressableAssetEntry asset = set.CreateOrMoveEntry(Guid, Group); if (isSimplied) { asset.address = Path.GetFileNameWithoutExtension(assetPath); } else { asset.address = assetPath; } asset.SetLabel(lableName, true, true); }

????具体思路:
????????根据文件夹路径,获取该文件夹下的所有资源的路径信息,调用添加分组接口,检查是否存在当前分组,若无,则创建AddressableAssetGroup类型分组对象,设置默认状态,使用AssetDatabase.AssetPathToGUID获取当前路径资源的GUID,通过AddressableAssetSettingsDefaultObject.Settings.CreateOrMoveEntry(),创建AddressableAssetEntry对象,既勾选了addressable,再去简化资源地址和设置标签。
11. 内存管理 资源加载
????Addressables.LoadAssetAsync();单个资源
????Addressables.LoadAssetsAsync();多个资源
????Addressables.LoadSceneAsync();场景的加载
????GamoeObject实例化加载
????Addressables.InstantiateAsync();实例化加载
????GameObject.Instantiate();Unity提供实例化方法
资源卸载
????Addressables.UnloadSceneAsync();场景的卸载
????Addressables.Release();释放资源,参数是资源或????AsyncOperationHandle句柄
????Addressables.ReleaseInstance();销毁Addressable系统创建的实例
注:Addressables.InstantiateAsync()和其他加载调用的另一个区别就是有一个可选的trackHandle参数,当设置为false时,就必须通过AsyncOperationHandle句柄来释放资源,而不能再通过AsyncOperationHandle.Result加载资源释放了。
引用计数问题: 资源卸载,可手动和自动。
????手动卸载,Addressable系统加载和卸载资源都是成对存在的,使用Addrsssables.Release()或Addressables.ReleaseInstance()方法卸载资源,减少引用计数。当资源的引用计数为0时,该资源就准备好卸载了,并减少了所有依赖项的引用计数。
????自动卸载,包含它的场景关闭时允许自动清理。
卸载问题
????若使用Addressables.ReleaseInstance()传入的实例并不是Addressables系统API创建的,或者是通过句柄创建实例,系统会检测到并返回false,以指示该方法无法释放指定的实例。在这种情况下,实例不会被销毁。
Addressables.LoadAsset()和Addressables.InstantiateAsync()讨论
????Addressables.InstantiateAsync()有一些相关的开销,所以如果需要在每一帧中实例化数百次相同的对象,可以考虑通过Addressables.LoadAsset()方法加载,然后通过GameObject.Instantiate()实例化。缺点是Addressables系统不知道您创建了多少实例,如果管理不当,可能会导致内存问题。例如,一个Prefab引用了一个加载不正确或者已经卸载的纹理,会导致渲染问题(或更糟)。这类问题很难跟踪,因为您可能不会立即触发内存卸载 。?
清除内存
????不再被引用的资源并不一定意味着资源产已被卸载。一个常见的应用场景涉及到一个资源包中包含多个资源。例如:
?????您有三个资源(“树”,“坦克”,“牛”)在同一个资源包(“东西”)。
?????当“树”加载时,“树”的ref-count +1,“东西”的ref-count +1。
?????稍后,当“坦克”加载时,“树”和“坦克”的ref-count均为1,并且“东西”包的ref-count为2。
?????如果你释放“树”,它的ref-count就会变成0。
????在这个例子中,“树”资源实际上并没有被卸载。您可以加载资源包或其部分内容,但不能部分卸载资源包。在包本身完全卸载之前,所有资产都不会卸载。这个规则的例外是Resources.UnloadUnusedAssets,在上述场景中执行此方法将导致树卸载。因为Addressables系统不能识别这些事件, 只反映Addressables的ref-counts (不完全反映内存中存在的内容)。注意,如果您选择使用Resources.UnloadUnusedAssets,这是一个非常慢的操作,应该只在一个不会显示任何游戏内容的屏幕调用(比如加载屏幕)。
12. Addressable打包粒度 ????我们在使用Addressable系统时,需要考虑的是:需要多少个Group?这个Group里面放什么资源?打包方式是Pack Together 或者 Pack Together By Label 或者 Pack Separately? 很显然,这个跟使用Assetbundle是一样的,需要开发人员自己来规划。这不是因为Addressable不够强大,而是这是跟具体项目有关,每个项目的情况各不相同。

    推荐阅读