可寻址资源的主要优点之一,是将如何规划、构建和加载进行分离。以前这些都是紧密地联系在一起。
1、传统资源管理
如果在 Resources 目录中规划内容,该内容将内置到应用程序中初始包里,并且必须使用Resources.Load 提供资源路径的方法来加载内容。
要访问存储在其他位置的内容,则使用 直接引用 或资源 bundles。
如果使用资源 bundles,则将再次按路径加载根据一些策略组合起来。如果资源 bundles是远程的,或依赖于其他 bundles,则必须编写代码来管理所有 bundles 的下载、加载和卸载。
2、可寻址资源管理
为资源提供地址,无论您在项目中的任何位置或如何构建资源,都可以使用该地址加载该地址。
您可以更改可寻址资源的路径或文件名而不发生问题。您还可以将可寻址资源从Resources文件夹或本地构建目标移动到其他构建位置(包括远程构建位置),而无需更改加载代码。
资源组结构(Asset group schemas)
schemas定义了一组数据。你可以在 Inspector 上把一个schemas绑定到一个Asset Groups上。附加到组的schemas定义了构建它下面的资源内容。
例如,在打包模式下构建时,带有 BundledAssetGroupSchema 模式的组将充当资源 bundles的来源。您可以将schemas组合到用于定义新组的模板中。您可以通过 AddressableAssetsSettings 检查器添加架构模板。可以将模式集组合到用于定义新组的模板中。
3、构建脚本
构建脚本ScriptableObject 实现IDataBuilder接口的资源在项目中。用户可以创建自己的构建脚本,然后AddressableAssetSettings通过其检查器将它们添加到对象中。要在Addressables Groups 窗口(Window > Asset Management> Addressables > Groups)中应用构建脚本,请选择“Play Mode Script”,然后选择一个下拉选项。当前,已实现三个脚本来支持完整的应用程序构建,并提供三个用于在编辑器中进行迭代的播放模式脚本。
播放模式脚本(Play mode scripts)
可寻址资源包具有三个构建脚本,这些脚本创建播放模式数据以帮助您加速应用程序开发。
-
使用资源数据库(更快):Use Asset Database (faster)
使用资源数据库模式(BuildScriptFastMode)可让您在游戏流程中快速运行游戏。它直接通过资产数据库加载资产,无需分析或创建资产束即可进行快速迭代。 -
模拟组(高级):Simulate Groups (advanced)
“模拟组”模式(BuildScriptVirtualMode)可以分析内容的布局和相关性,而无需创建资源 bundles。资源通过从资源数据库ResourceManager加载,就好像它们是通过 bundles 加载的一样。要查看游戏过程中 bundles 何时加载或卸载,请在“可寻址事件查看器”窗口(Window > Asset Management > Addressables > Event Viewer)中查看资产使用情况。
“模拟组”模式可帮助您模拟加载策略并调整内容组,以找到生产版本的适当平衡。 -
使用现有版本:Use Existing Build(requires built groups)
使用现有构建模式最接近于已部署的应用程序构建,但是它要求您将数据构建为一个单独的步骤。
如果您不修改资源,则此模式是最快的,因为它在进入播放模式时不会处理任何数据。您必须通过选择 Build > New Build > Default Build Script 在 Addressables Groups窗口中(Window > Asset Management > Addressables > Groups)或使用游戏脚本中使用AddressableAssetSettings.BuildPlayerContent()方法,构建该模式内容。
4、分析与调试(Analysis and debugging)
默认情况下,可寻址资源仅记录警告和错误。
通过打开编辑器“Player settings”窗口(Edit > Project Settings… > Player),导航到 Other Settings > Configuration 部分,然后在 Scripting Define Symbols 字段中添加ADDRESSABLES_LOG_ALL,可以启用详细的日志记录。
您还可以通过在AddressableAssetSettings对象检查器中取消检查Log Runtime exceptions选项来禁用异常。
如果需要,您可以使用自己的异常处理程序实现ResourceManager.ExceptionHandler属性,但是这应该在Addressables完成运行时初始化完成之后(参见下文)。
5、初始化对象
您可以将对象附加到 AddressableAssetSettings,并在运行时将其传递给初始化过程。该CacheInitializationSettings对象在运行时控制Unity的缓存API。
要创建自己的初始化对象,请创建实现IObjectInitializationDataProvider接口的ScriptableObject 。这是负责创建与运行时数据一起序列化ObjectInitializationData的编辑器组件。
6、自定义URL值
在几种情况下,您将需要在运行时自定义资源(通常是AssetBundle)的路径或URL。最常见的示例是创建签名的URL。另一个可能是动态主机确定。
下面的代码是将查询字符串附加到所有URL的示例:
//Implement a method to transform the internal ids of locations
string MyCustomTransform(IResourceLocation location)
{if (location.ResourceType == typeof(IAssetBundleResource) && location.InternalId.StartsWith("http"))return location.InternalId + "?customQueryTag=customQueryValue";return location.InternalId;
}//Override the Addressables transform method with your custom method. This can be set to null to revert to default behavior.
[RuntimeInitializeOnLoadMethod]
static void SetInternalIdTransform()
{Addressables.InternalIdTransformFunc = MyCustomTransform;
}
** 请注 **:当构建的视频文件转换成Addressables的意图加载它们在Android平台上,你必须创建一个CacheInitializationSettings对象,禁用该对象上Compress Bundles,然后将其添加到AddressableAssetSettings对象的初始化对象列表(如果还没有这样做的话)。
7、内容更新工作流程
Unity建议将游戏内容分为两类:
- 静态:永远不会更新的内容。
- 动态:希望更新的内容。
在这种结构中,静态内容随应用程序一起提供(或在安装后立即下载),并且驻留在很少的大 bundles 中。动态内容驻留在线,最好放在较小的 bundles 中,以最大程度地减少每次更新所需的数据量。
可寻址资源系统的目标之一是使这种结构易于使用和修改,而无需更改脚本。但是,当您不想发布全新的应用程序内部版本时,“可寻址资源系统”也可以适应需要更改“静态”内容的情况。
请注意:在不允许远程更新的情况下(例如:许多当前的视频游戏机或没有服务器的游戏),您应该每次都进行完整而全新的构建。
1.如何运行(How it works)
Addressables使用内容目录将地址映射到每个资产,并指定在何处以及如何加载它。为了向您的应用程序提供修改映射的能力,您的原始应用程序必须知道此目录的在线副本。
要进行设置,请在AddressableAssetSettings检查器上启用构建远程目录(Build Remote Catalog)设置。
这确保将目录的副本构建到指定路径并从指定路径加载。应用程序发布,这个加载路径就不能改变。内容更新过程创建目录的新版本(具有相同的文件名),以覆盖前面指定的加载路径上的文件。
构建应用程序会生成一个唯一的应用程序内容版本字符串,该字符串标识每个应用程序应加载的内容目录。给定的服务器可以包含应用程序多个版本的目录,而不会发生冲突。我们将所需的数据存储在 addressables_content_state.bin 文件中。这包括版本字符串,以及标记为的组中包含的任何资源的哈希信息 StaticContent。
默认情况下,它位于 Assets/AddressableAssetsData/ 项目目录中,其中是您的目标平台。
addressables_content_state.bin 文件包含每个哈希和依赖信息StaticContent的Addressables系统资源组。构建到 StreamingAssets 文件夹的所有组都应标记为 StaticContent,尽管大型远程组也可以从该名称中受益。在下一步(准备更新内容,如下所述)中,此哈希信息将确定是否有任何 StaticContent 组包含已更改的资源,因此需要将这些资源移至其他位置。
2.唯一BundleID(Unique Bundle IDs)
当将AssetBundle加载到内存中时,Unity强制使用相同的内部名称不能加载两个包。这可能对在运行时更新包造成一些限制。既然Addressables支持在初始化之外更新目录,则可以更新已经加载的内容。
要实现这一目标,必须做到以下两点之一:
- 选择是在更新目录之前卸载所有 Addressables 内容。
- 选项是确保更新后的资源包具有唯一的内部标识符。
这将允许您在旧包仍在内存中时加载新包。我们从第一个选项来启用第二个选项。在AddressableAssetSettings检查器中打开“Unique Bundle IDs”。此选项的缺点是需要重新构建依赖项链上的 bundles 。意思是如果你在一个组中改变了一个材质,默认情况下只有材质包会被重建。使用“Unique Bundle IDs”,引用该材料的任何资源都需要重新构建。
3.准备内容更新
如果您在任何StaticContent组中都修改了资源,则需要运行检查内容更新限制(Check for Content Update Restrictions)命令。这会将所有修改后的资源从静态组中移出,并将它们移至新组。 生成新资源组:
- 在Unity编辑器中打开 Addressables Groups 窗口(Window > Asset Management > Addressables> Groups)。
- 在 Addressables Groups 窗口中,选择顶部菜单栏上的 Tools,然后选择检查内容更新限制(Check for Content Update Restrictions)。
- 在打开的构建数据文件(Build Data File)对话框中,选择addressables_content_state.bin文件(默认情况下,该文件位于:Assets/AddressableAssetsData/项目目录中,其中是您的目标平台)。
此数据用于确定自上次构建应用程序以来已修改了哪些资源或依赖项。系统将这些资源移至新的组,以准备内容更新构建。
注意:如果所有更改都限于非静态组,则此命令将不执行任何操作。
重要提示:在运行准备操作之前,Unity建议分支您的版本控制系统。准备操作以适合于更新内容的方式重新排列资源组。分支机构确保下次发不新player时,您可以快速切回到自定义的设定。
4.构建内容更新
要构建内容更新:
- 在Unity编辑器中打开 Addressables Groups 窗口(Window > Asset Management > Addressables> Groups)。
- 在 Addressables Groups 窗口中,在顶部菜单中选择构建(Build),然后选择更新以前的构建(Update a Previous Build)。
- 在打开的构建数据文件(Build Data File)对话框中,选择现有应用程序构建的构建文件夹。构建文件夹必须包含一个addressables_content_state.bin文件(默认情况下,该文件位于Assets/AddressableAssetsData/项目目录中,其中是您的目标平台)。
构建生成内容目录、哈希文件和资源bundles。
生成的内容目录与所选应用程序构建中的目录具有相同的名称,将覆盖旧的目录和散列文件。应用程序加载散列文件以确定是否有新的目录可用。系统从应用程序“附带的或已经下载的”已存在bundles加载未修改的资源。
系统使用addressables_content_state.bin文件中的内容、版本、字符串和位置信息来创建资源bundles。不包含更新内容的资源 bundles 的文件名和原来的一样。如果一个资源 bundle 包含更新的内容,则生成一个新的资源 bundle,其中包含更新的内容,并有一个新的文件名,以便它可以与原始文件共存。带有新文件名的资源 bundles 必须复制到指定的内容托管位置。
系统还为静态内容构建资源 bundles,但是无需将其上传到内容托管位置,因为没有可寻址资源资源记录引用它们。
请注意:您不应在构建新 player 的时候,进行内容更新(e.g., player code, addressables)更改生成脚本。这可能会在您的应用程序中导致无法预料的行为。
5.在运行时检查内容更新
您可以添加自定义脚本来定期检查是否有新的可寻址内容更新。使用以下函数调用开始更新:
public static AsyncOperationHandle<List<string>> CheckForCatalogUpdates(bool autoReleaseHandle = true)
其中List包含已修改的定位器ID列表。您可以过滤此列表以仅更新特定的ID,或将其完全传递到 UpdateCatalogs API。
如果有新内容,则可以向用户显示一个按钮来执行更新,也可以自动执行。请注意,由开发人员来确保释放过时的资源。
目录列表可以为空,如果是,则以下脚本更新所有需要更新的目录:
public static AsyncOperationHandle<List<IResourceLocator>> UpdateCatalogs(IEnumerable<string> catalogs = null, bool autoReleaseHandle = true)
返回值是更新的定位器列表。
6.内容更新示例
在此示例中,已发布的应用程序知道以下几组:
Local_Static | Remote_Static | Remote_NonStatic |
---|---|---|
AssetA | AssetL | AssetX |
AssetB | AssetM | AssetY |
AssetC | AssetN | AssetZ |
由于该版本已上线,因此有些玩家的设备上有 Local_Static,并且可能有一个或两个远程bundles 缓存在本地。
如果您修改了每个组(AssetA,AssetL和AssetX)中的一项资源,然后运行Check for Content Update Restrictions,则本地可寻址设置中的结果为:
Local_Static | Remote_Static | Remote_NonStatic | content_update_group (non-static) |
---|---|---|---|
AssetX | AssetA | ||
AssetB | AssetM | AssetY | AssetL |
AssetC | AssetN | AssetZ |
请注意:prepare operation 实际上是编辑静态组,这可能会与正常的理解有冲突。但是,关键是系统可以构建上述布局,但是会丢弃任何静态组的构建结果。因此,从玩家的角度出发,您将获得以下结果:
Local_Static |
---|
AssetA |
AssetB |
AssetC |
Local_Static bundle 已经在玩家设备上,您无法更改。不再引用此旧版本的AssetA。相反,它作为无效数据残留在玩家设备上。
Remote_Static |
---|
AssetL |
AssetM |
AssetN |
Remote_Static bundle 没有改变。如果它还没有缓存在玩家的设备上,它将在AssetM或AssetN被请求时下载。像AssetA一样,这个旧版本的AssetL不再被引用。
Remote_NonStatic (旧) |
---|
AssetX |
AssetY |
AssetZ |
Remote_NonStatic bundle 是现在很旧了。您可以从服务器中删除它,但无论如何,从现在开始都不会下载它。如果被缓存,它将最终离开缓存。像AssetA和AssetL一样,AssetX不再引用此旧版本。
Remote_NonStatic (新) |
---|
AssetX |
AssetY |
AssetZ |
旧的 Remote_NonStatic bundle 将替换为新版本,以其哈希文件来区分。修改后的版本AssetX将使用此新 bundle 进行更新。
content_update_group |
---|
AssetA |
AssetL |
content_update_group bundle 包含修改后的资产,这些资源将在以后被引用。
请注意,上面的示例具有以下含义:
- 任何更改后的本地资源将永远在用户的设备上保持未使用状态。
- 如果用户已经缓存了非静态 bundle,则他们将需要重新下 bundle,包括未更改的资产(在这种情况下,例如AssetY和AssetZ)。理想情况下,用户没有缓存 bundle,在这种情况下,他们只需要下载新Remote_NonStatic bundle 即可。
- 如果用户已经缓存了Static_Remote包,则只需下载更新的资源(在这种情况下,通过content_update_group 下载 AssetL)。在这种情况下,这是理想的。如果用户没有缓存 bundle,则必须通过content_update_group下载新的AssetL,并通过未引用的 Remote_Static bundle 下载现已不存 在的AssetL。无论初始缓存状态如何,在某个时候,用户都将在其设备上保存已失效的文件AssetL,即使从未访问过,它也会被永久的缓存。
远程内容的最佳设置将取决于您怎么去使用。