Unity Addressable 可寻址插件

AA,全名叫做Addressable,可寻址资源,可以将一个完整的游戏资源进行拆分,达到减小安装包和内存使用的的目的。

安装与初始化

  • 安装:Windows -> Package Manager,搜索Addressable并安装。

img

  • 初始化:Window -> Asset Management -> Addressables -> Group

img

之后在Assets文件夹下会多一个叫做AddressableAssetData的文件夹

AddressableAssetData目录

img

根目录下的AddressableAssetSetting中保存的是所有的AA基础配置

AssetGroups目录下的是当前的Group,初始化以后,默认是有两个,BuildInData和DefaultLocalGroup

AssetGroups/Schemas下面的是三个schema,第一个属于第一个Group,后两个属于第二个Group

AssetGroupTemplate下面的是Group的Template,也就是我们新建Group时候的模版,默认只有一个PackdAsset,也就是我们新建的时候只能选择从这个模版新建,或者新建一个空的Group

DataBuilders内部是四个BuildScript,是Build或者PlayMode的脚本,需要先配置在AA基础配置中(我们也可以写自己的BuildScript,然后在这里添加以后可以使用),即

img

然后可以在PlayMode中使用(前三个)

img

以及Build中使用(第四个)

img

基础配置

Profile:配置打包的地址和Target平台变量

img

img

  • Square brackets [ ]: Addressables 在构建时评估方括号包围的条目。 这些条目可以是其他配置文件变量(例如 [BuildTarget])或代码变量(例如 UnityEditor.EditorUserBuildSettings.activeBuildTarget)。 在构建期间,当它处理您的组时,Addressables 评估方括号内的字符串并将结果写入目录。

  • Curly brackets { }: Addressables 在运行时评估用大括号括起来的条目。 您可以使用运行时类的代码变量(例如 {UnityEngine.AddressableAssets.Addressables.RuntimePath})

  • [BuildTarget] 方括号里可以使用Unity的全局变量

  • {Addressable.AddressableConfig.server_android_url} 大括号中可以直接引用类中的静态变量

  • BuildTarget:打包的目的平台

  • LocalBuildPath:本地资源打包的路径

  • LocalLoadPath:本地资源的加载路径

  • RemoteBuildPath:远程资源的打包路径

  • RemoteLoadPath:远程资源的加载路径

启用Remote资源发布

启用Build Remote Catalog,然后Build & Load Paths设置为custom,就可以选择上面说的profile中的五个变量了,这里能够显示根据Profile中的配置计算出来的实际路径。

比如,我们目前的BuildTarget是OSX,所以此时RemoteBuildPath的ServerData/[BuildTarget]计算的结果就是ServerData/StandaloneOSX

img

不启动这个也可以,照样可以打包Local和Remote的资源,并且也可以加载远程的资源,但是如果不启动这个,就不能为Remote资源的单独生成带有时间戳或者版本号的Catalog(本来就会在streamAsset文件夹下有一个包含Local和Remote的Catalog),也就不能根据Remote的Catalog去不更新App而单独更新资源,详细内容参考这里:Remote content distribution | Addressables | 1.18.16

创建新的Group Template(可选)

我们创建新的Group时,需要根据从某个Template来创建。默认在AddressableAssetData/AssetGroupTemplates下有一个PackedAsset的模版,所以我们在create 新的 group时只有一个选项:

img

新建Template

如果您创建自己的构建脚本或需要额外设置的实用程序,您可以在您自己的架构对象中定义这些设置并创建您自己的组模板:

  • 使用“项目”面板导航到 Assets 文件夹中的所需位置。
  • 创建一个空白组模板(menu: Assets > Addressables > Group Templates > Blank Group Templates)。
  • 为模板指定一个合适的名称。
  • 如果需要,在“Inspector”窗口中添加说明。
  • 单击添加架构按钮并从架构列表中进行选择。
  • 继续添加schema,直到所有必需的模式都添加到列表中。

如果您使用默认构建脚本,则组必须使用内容打包和加载架构。 如果您使用内容更新版本,则组必须包含内容更新限制架构。 有关更多信息,请参阅构建。

为Template添加Schema

The built-in schemas include:

  • Content Packing & Loading: this is the main Addressables schema used by the default build script and defines the settings for building and loading Addressable assets.

  • Content Update Restrictions: defines settings for making differential updates of a previous build. See Builds for more information about update builds.

  • Resources and Built In Scenes: a special-purpose schema defining settings for which types of built-in assets to display in the Built In Data group.

这三个Schema在Library/PackageCache/[email protected]/Editor/Settings/GroupSchemas中

img

创建自定义的Schema

To create your own schema, extend the AddressableAssetGroupSchema class (which is a kind of ScriptableObject).

1
2
3
4
5
6
7
8
9
10
11
using UnityEditor.AddressableAssets.Settings;



public class __CustomSchema __: AddressableAssetGroupSchema

{

public string CustomDescription;

}

Once you have defined your custom schema object, you can add it to existing groups and group templates using the Add Schema buttons found on the Inspector windows of those entities.

创建新的Group用于打包为远程bundle

如果上一步创建了Template,则可以直接根据Template创建Group,如果没有,则选择创建Blank的Group,然后通过Add Schema,添加两个基本的schema

img

然后将buildPath和loadPath设置为remote的。

这个时候,我们就可以看到

img

多了两个schema,即为我们刚才add的两个schema

将资源标记为可寻址

选择某个资源,在Inspector中勾选Addressable

img

默认会将资源放到Default Local Group中

我们选择两个资源标记为可寻址,一个就放在默认位置,一个移动到我们新创建的Remote Group

img

如何管理AA资源

几个基本的参考原则:Managing Addressables in the Editor | Addressables | 1.18.16

AA资源打包后会占用多少体积

img

如果您在多个类别中使用相同的资产,那么 Unity 在构建时会制作资产的副本,而不是共享单个实例。 例如,如果您在内置场景中使用了一个材质,并且也在位于 Resources 文件夹中的预制件中使用了它,那么您最终会在构建中拥有该材质的两个副本——即使材质资产本身不是 位于资源。 如果您随后将同一材料标记为可寻址,则最终会得到三份副本。 (项目 StreamingAssets 文件夹中的文件永远不能被该文件夹外的资产引用。)

所以,尽量不使用Resources文件夹。

在构建播放器之前,您必须对可寻址资产进行内容构建。 在播放器构建期间,Unity 将您的本地 Addressables 复制到 StreamingAssets 文件夹,以便它们与您放置在 StreamingAssets 中的任何资产一起包含在构建中。 (这些资产在构建过程结束时被删除。)您有责任将内容构建生成的远程 Addressables 文件上传到您的托管服务。 有关更多信息,

当您将资产添加到 Addressables 组时,该资产会在您进行内容构建时打包到 AssetBundle 中。 如果资产引用其他资产(称为依赖项),则如何处理这些依赖项取决于它们是否也是可寻址的。 可寻址的依赖项根据它们所在组的设置打包到 AssetBundle 中——这可能是与引用资产相同的包或不同的包。 不可寻址的依赖项包含在其引用资产的包中。

如果多个 Addressable 引用相同的非 Addressable 资产,则非 Addressable 资产的副本包含在每个包含引用 Addressable 的包中。

img

当一项资产隐式包含在多个包中时,可能会发生一个微妙的后果,即可以在运行时实例化该资产的多个实例,而不是您的游戏逻辑所期望的单个实例。 如果您在运行时更改实例状态,则只有来自同一包的对象才能看到更改,因为所有其他资产现在都有自己的单独实例,而不是共享公共实例。

为了消除这种重复,您可以将依赖项设为可寻址资产并将其包含在现有包之一中或将其添加到不同的包中。 一旦您使依赖项成为可寻址的,每当您加载引用它的可寻址对象之一时,就会加载它所属的包。

请注意,当您引用另一个包中的资产时,必须 在加载当前包中的任何资产时加载该包,而不仅仅是包含引用的资产。 尽管没有加载其他 AssetBundle 中的任何资产,但加载包有其自身的运行时成本

AA资源应该拆分多少Group

过多的危险:

  • 每个包都有内存开销。 这与该页面上概述的许多因素有关,但简而言之,这种开销可能很大。 如果您预计一次将 100 个甚至 1000 个包加载到内存中,这可能意味着大量内存被消耗掉。
  • 下载包有并发限制。 如果您同时需要 1000 个捆绑包,则不可能同时下载所有捆绑包。 将下载一些数字,当它们完成时,将触发更多。 在实践中,这是一个相当小的问题,如此小以至于您通常会受到下载的总大小的限制,而不是它被分成多少个包。
  • 捆绑信息会使目录膨胀。 为了能够下载或加载目录,我们会存储有关您的捆绑包的基于字符串的信息。 数以千计的数据包可以大大增加目录的大小。
  • 重复资产的可能性更大。 假设两个材料被标记为可寻址,并且每个都依赖于相同的纹理。 如果它们在同一个包中,则纹理被拉入一次,并被两者引用。 如果它们在单独的包中,并且纹理本身不是可寻址的,那么它将被复制。 然后您需要将纹理标记为可寻址,接受重复,或将材料放在同一个包中。

捆绑太少的危险:

  • UnityWebRequest(我们用来下载)不会恢复失败的下载。 因此,如果正在下载大包并且您的用户失去连接,则一旦他们重新获得连接,下载就会重新开始。

  • 物品可以从捆绑包中单独加载,但不能单独卸载。 例如,如果你在一个包中有 10 个材料,加载所有 10 个,然后告诉 Addressables 释放其中的 9 个,所有 10 个都可能在内存中。 有关更多信息,请参阅内存管理。

如何在运行时使用AA资源

一旦您将可寻址资产组织成组并内置到AssetBundles中,您仍然必须加载、实例化并最终在运行时发布它们。

首先是一些AA资源在内存中的行为,可以帮助我们更好地管理AA资源,从而优化内存的使用

内存管理

Addressables 系统通过保持它加载的每个项目的引用计数来管理用于加载资产和包的内存。

当一个 Addressable 被加载时,系统增加引用计数;当资产被释放时,系统会减少引用计数。当一个 Addressable 的引用计数归零时,它就有资格被卸载。当您显式加载可寻址资产时,您还必须在完成后释放该资产。

避免“内存泄漏”(不再需要后仍保留在内存中的资产)的基本经验法则是将每次对加载函数的调用与对释放函数的调用进行镜像。您可以使用对资产实例本身的引用或使用原始加载操作返回的结果句柄来释放资产。

但是请注意,释放的资产不一定会立即从内存中卸载。资产使用的内存在它所属的 AssetBundle 也被卸载之前不会被释放。(释放的资产也可以通过调用Resources.UnloadUnusedAssets卸载,但这往往是一个缓慢的操作,可能会导致帧速率故障。)

AssetBundle 有自己的引用计数(系统将它们视为可寻址对象,并将它们包含的资产视为依赖项)。当您从包中加载资产时,包的引用计数会增加,而当您释放资产时,包的引用计数会减少。当一个包的引用计数归零时,这意味着它包含的所有资产都没有在使用,并且包和它包含的所有资产都从内存中卸载。

使用事件查看器来监控您的运行时内存管理。查看器显示加载和卸载资产及其依赖项的时间。

避免资产churn

如果您释放恰好是 AssetBundle 中最后一项的对象,然后立即重新加载该资产或捆绑包中的其他资产,则可能会出现资产churn问题。

例如,假设你有两种材料,boat并且plane该份额纹理,cammo,已被拉入了自己的AssetBundle。1boat级使用和 2 级使用plane。当您退出第 1 级时,您释放boat并立即加载plane. 当您释放时boat,Addressables 卸载纹理cammo。然后,当您加载 时plane,Addressables 会立即重新加载cammo

内存使用

当你加载一个 AssetBundle 时,Unity 会分配内存来存储包的内部数据,这是除了用于它包含的资产的内存之外。加载的 AssetBundle 的主要内部数据类型包括:

当你组织你的 Addressable 组和 AssetBundle 时,你通常必须在你创建和加载的 AssetBundle 的大小和数量之间进行权衡。一方面,更少、更大的包可以最大限度地减少 AssetBundles 的总内存使用量。另一方面,使用大量小包可以最大限度地减少峰值内存使用量,因为您可以更轻松地卸载资产和 AssetBundles。

虽然磁盘上 AssetBundle 的大小与其运行时的大小不同,但您可以使用磁盘大小作为构建中 AssetBundle 内存开销的大致指南。您可以从Build Layout Report获取包大小和其他可用于帮助分析 AssetBundles 的信息。

加载可寻址资产

Addressables类提供了加载寻址资产的几种方法。您可以一次或批量加载一个资产。要识别要加载的资产,您可以将单个键或键列表传递给加载函数。键可以是以下对象之一:

  • 地址:包含您分配给资产的地址的字符串

  • 标签:包含分配给一个或多个资产的标签的字符串

  • AssetReference对象:实例AssetReference

  • IResourceLocation实例:一个中间对象,包含加载资产及其依赖项的信息。

当您调用资产加载功能之一时,Addressables 系统开始执行以下任务的异步操作:

  1. 查找指定键的资源位置(IResourceLocation 键除外)
  2. 收集依赖项列表
  3. 下载所需的任何远程 AssetBundle
  4. 将 AssetBundles 加载到内存中
  5. 将操作的Result对象设置为加载的对象
  6. 更新操作的状态并调用任何 Completed事件侦听器

如果加载操作成功,Status 将设置为 Succeeded,并且可以从Result对象访问加载的对象。

如果发生错误,则将异常复制到操作对象的OperationException成员中,并将 Status 设置为 Failed。默认情况下,异常不会作为操作的一部分抛出。但是,您可以为ResourceManager.ExceptionHandler属性分配一个处理程序函数来处理任何异常。此外,您可以在 Addressable 系统设置中启用Log Runtime Exceptions选项以将错误记录到 Unity控制台

当您调用可以加载多个可寻址资产的加载函数时,您可以指定是否应该在任何单个加载操作失败时中止整个操作,或者该操作是否应该加载任何可以加载的资产。在这两种情况下,操作状态都设置为失败。(releaseDependenciesOnFailure在调用加载函数时将参数设置为 true 以在任何失败时中止整个操作。)

尝试构建全量打包

要构建您的内容工件:

  1. 配置您的组设置。
  2. 如果您要远程分发内容,请配置您的配置文件和可寻址系统设置以启用远程内容分发。
  3. 选择正确的配置文件。
  4. Groups 窗口启动构建。

由于我们没有创建自定义的Profile,暂时使用默认的Profile进行打包测试

默认配置的路径

默认本地路径

本地构建路径默认为Addressables.BuildPath提供的路径,它位于 Unity 项目的 Library 文件夹中。Addressables 根据您当前的平台构建目标设置将文件夹附加到本地构建路径。当您为多个平台构建时,构建会将每个平台的工件放在不同的子文件夹中。

同样,本地加载路径默认为Addressables.RuntimePath提供的路径,解析为 StreamingAssets 文件夹。再次 Addressables 将平台构建目标添加到路径中。

当您将本地包构建到默认构建路径时,构建代码会在您构建播放器时临时将工件从构建路径复制到 StreamingAssets 文件夹(并在构建后删除它们)。

如果您构建到自定义本地路径或从自定义本地路径加载,则您有责任在进行播放器构建之前将构建工件复制到项目中的正确位置,并确保您的应用程序可以在运行时访问这些工件。

默认远程路径

Addressables 将默认远程构建路径设置为在您的项目文件夹下创建的任意选择的文件夹名称“ServerData”。构建将当前平台目标作为子文件夹添加到路径中,以分离不同平台的独特工件。

默认远程加载路径是“ http://localhost/ ”,附加了当前配置文件 BuildTarget 变量。您必须将此路径更改为计划加载可寻址资产的基本 URL。

根据您正在进行的开发、测试或发布类型,使用不同的配置文件来设置远程加载路径。例如,您可以有一个配置文件从 localhost 服务器加载资产用于一般开发构建,一个配置文件从临时环境加载资产用于 QA 构建,还有一个配置文件从您的内容交付网络 (CDN) 加载资产用于发布构建

打包结果

本地打包结果

img

img

可以看到content是拆分为了两个bundle,原因是本地打包的资源中有scene,这种含有场景的group会多分出一个bundle

远程内容打包结果

img

打包产物分析

本地打包的产物

因为我们目前只为OS打包了资源,所以aa文件夹下只有OSX

默认情况下,构建会在您的配置文件设置中为 LocalBuildPath 和 RemoteBuildPath 变量定义的位置创建文件。 Unity 用于您的播放器构建的文件包括 AssetBundles (.bundle)、目录 JSON 和哈希文件以及设置文件。

在大多数情况下,不应该从默认值更改本地构建或加载路径。如果这样做,则必须在进行Player构建之前将本地构建工件从自定义构建位置复制到项目的StreamingAssets文件夹。改变这些路径也会妨碍你将Addressable作为Player Build的一部分

如果我们将选择资源为RemoteBuildPath,那么需要我们自己去手动上传资源到CDN

内容构建还会创建以下文件,这些文件在Player构建中不会直接使用

  • addressables_content_state.bin: used to make a content update build. If you support dynamic content updates, you must save this file after each content release. Otherwise, you can ignore this file.

  • AddressablesBuildTEP.json: logs build performance data. See Build Profiling.

See Building Addressable content for more information about how to set up and perform a content build.

远程资源打包产物

远程打包的产物还会多一个hash,用于验证下载资源的正确性。

但是它的bin文件在Asset目录下。

产物的名字还会多一个时间戳,避免由于缓存而导致的更新不及时。

img

如果开启以上配置,则产物名中的就不是时间戳而是版本号。

尝试增量式更新

Local的bundle不需要这种,因为文件内容全部都打包在Application中了。

当您远程分发内容时,您可以对先前发布的构建执行差异更新,以最大限度地减少用户必须下载的数据量(与完整构建相比)。

正确配置远程组并拥有包含远程内容的先前构建后,您可以通过以下方式执行内容更新构建:

除了开启Build Remote Catalog,对于每个Group的更新配置还有Content update builds Setting | Addressables | 1.18.16

一个是Content Update Restriction是Cannot 还是 Can Change Post Release,Cannot的叫做Static

另一个是Unique Build ID,开启后可以让用户在不重启App的情况下更新资源。

  1. Run the Check for Content Update Restrictions tool.

这一步有可能显示没有更改,因为它不检测Static的Group,因为只有Static的才需要创建一个Content Update的Group,而Non-Static的直接就覆盖原有的bundle了,具体可以参考这里:Content update builds Example | Addressables | 1.18.16

Important: Before you run the Check for Content Update Restrictions tool, you should make a branch with your version control system. The tool rearranges your asset groups in a way suited for updating content. Branching ensures that next time you ship a full player build, you can return to your preferred content arrangement.

  1. 打开Groups 窗口 (菜单:Windows > Asset Management > Addressables > Groups)。
  2. 从工具栏上的配置文件菜单中选择所需的配置文件。
  3. Build菜单中选择Update a Previous Build文件选择器对话框打开。

img

  1. 找到addressables_content_state.bin您正在更新的构建生成的文件。(默认位置在您的Assets/AddressableAssetsData/TargetPlatform文件夹中。)
  2. 单击打开以启动更新构建。

这种构建方式需要上一次的全量构建产生的bin文件,也就是说,bin文件只会在全量构建时产生和修改,而Update a Previous Build中的这个Previous Build指的永远是上一次全量构建,即使你中间进行过其他的增量式构建。

全量式构建是在每次更新整个Application时运行的,而如果你只想更新AA资源而不重新发版App,则只需要在全量构建时选择Build Remote Catalog,这样就会单独再为Remote资源构建一个catalog,每次拉取Remote资源时,会自动检测远程是否有新版的资源

You must save the addressables_content_state.bin file produced by the Default Build Script for each build that you intend to update in the future. This file is updated every time you run the build script. Be sure to save the version produced for the content build that you publish.

本地环境搭建

新建Editor Host

当我们使用Remote资源时,默认的LoadPath其实并没有启动服务,为了在本地模拟这个情况,AA提供了Editor Host

img

创建一个新的host

img

选择enable之后,就会启动一个服务器

img

此时的服务启动,我们成功托管了bundle在本地的8888端口,但是现在的问题是我们的Profile中的RemoteBuildPath仍然是 http://localhost/[BuildTarget],如果我们使用者变量,我们将无法正确加载,所以我们需要创建一个新的Profile,并修改其RemoteLoadPath为本的的8888端口。

新建Editor Hosted Profile

在开发期间使用托管服务时,请考虑创建一个配置文件,将资产组配置为从托管服务加载。有关配置文件的更多信息,请参阅可寻址资产配置文件

进入Addressables Profiles窗口后,通过Create > Profile创建一个新的配置文件。在以下示例中,新配置文件称为“Editor Hosted”。

修改远程加载 URL 以从托管服务加载。在Addressables Hosting窗口中,您可以使用远程LoadPath变量中名为[PrivateIpAddress]和的字段来构建路径 URL(例如,)。http://[PrivateIpAddress]:[HostingServicePort]/[BuildTarget]

img

配置服务的配置文件。

验证是否正确配置了每个组。确保您将BuildPathLoadPath路径设置为您修改后用于托管服务的各自配置文件键。在此示例中,您可以看到应如何扩展LoadPath 中的配置文件变量以构建正确的基本 URL,以便从托管服务加载资产。

img

然后切换当前激活的Profile为Editor Hosted。

修改Play Mode Script

img

  • Use the Asset database: Addressables loads Assets directly from the Asset database. This option typically provides the fastest iteration speed if you are making both code and Asset changes, but also least resembles a production build.

  • Simulate groups: Addressables loads Assets while simulating groups. This option is helpful if you are working on organizing and optimizing your Addressables groups themselves. It provides Addressables events without requiring a full content rebuild after every change.

  • Use existing build: Addressables loads content from your last content build. This option most resembles a production build and can provide fast iteration turnaround if you aren’t changing Assets.

新建生产环境配置

对于生产环境,我们还需要创建一个Profile

img

不要忘记将BuildPath下的内容上传到LoadPath中

CI/CD搭建

打包所需要的脚本:AddressableAssetSettings.BuildPlayerContent | Addressables | 1.15.1

之所以用1.15版本的的文档,是因为1.18的文档里面没有改API的使用介绍了

高级教程

其他配置:Addressable Asset Settings | Addressables | 1.18.16

资源管理:Managing Addressables in the Editor | Addressables | 1.18.16

资源打包:Building content | Addressables | 1.18.16

远程资源分发:Remote content distribution | Addressables | 1.18.16

分析工具:Diagnostic tools | Addressables | 1.18.16

资源预加载:Preloading dependencies | Addressables | 1.18.16,[AssetBundle Loading | Addressables | 1.18.16](