Unity全新的资源管理流程Addressable Asset System(寻址式资源管理)可以让你在Unity编辑或运行时来管理各种资源加载分配。它是一个基于Asset Bundle架构延伸出来的高级管理系统。非常适合非程序人员来使用,意味着未来打包与管理资源的工作也可以让非程式人员来分担,对管理大型项目或结构复杂的资源特别有帮助。
什么是asset?
资产是您用于创建游戏或应用程序的内容。资产的常见示例包括预制件(Prefabs),纹理(textures),材料(materials),音频剪辑(audio clips)和动画(animations)。
什么是Addressable Asset?
使资产“可寻址”允许您使用该资产的唯一地址从任何地方调用它。无论该资产是在 本地应用程序中 还是在 内容交付网络(资源服务器) 上,可寻址资产系统都会找到并返回该资产。您可以通过其地址加载单个可寻址资产,或使用您定义的 自定义组标签 加载许多可寻址资产。
为什么要使用可寻址资产?
Addressable 缩短了迭代周期:
迭代时间
: 通过其地址引用类型非常有效.对内容的优化不需要更改代码.依赖关系管理
: 系统返回所请求内容的所有依赖关系, 方便在内容返回之前加载所有依赖的网格,着色器,动画等.内存管理
: 系统的卸载资产并加载它们,自动计算引用并提供强大的分析器以帮助您发现潜在的内存问题。内容打包
: 由于系统映射并理解复杂的依赖关系链, 因此即使在移动或重命名资产时,也可以有效地打包捆绑包。您可以轻松地为本地和远程部署准备资产,以支持可下载内容和减少应用程序大小。
概述
可寻址资产系统包括两个包:
- Addressable Assets package (主要包)
- Scriptable Build Pipeline package (依赖)
安装Addressable Assets包时,Scriptable Build Pipeline包将同时安装。
概念
Address(地址)
: 资产的位置标识符,便于运行时检索.AddressableAssetData directory(可寻址资产元数据目录)
: 将您的可寻址资产元数据存储在Project的Assets目录.Asset group(资产组)
: 一组可用于 构建时处理的 可寻址资产.Asset group schema(资产组架构)
: 定义一组数据,您可以将这些数据分配给组并在构建期间使用。AssetReference(资产引用)
: 一个对象,其操作类似于直接引用,但具有 延迟初始化。该AssetReference对象将GUID存储为可按需加载的Addressable。Asynchronous loading(异步加载)
: 允许在整个开发过程中更改资产及其依赖项的位置,而无需更改游戏代码。异步加载是可寻址资产系统的基础。Build script(构建脚本)
: 运行 资产组处理器 以打包资产,并提供资源管理器的地址和资源位置之间的映射。Label(标签)
: 为类似项的运行时加载提供附加的 可寻址资产标识符 (例如,Addressables.DownloadDependenciesAsync("spaceHazards");
).
入门
重要提示:可寻址资产系统需要Unity版本2018.3或更高版本
使Asset成为AddressableAssets
两种标记方式:
- 在对象的
Inspector
面板中 -> 单击Address
复选框 -> 输入用于标识资产的名称。
- 选择 Window >
Asset Management
>Addressables
去打开Addressables window
. 然后, 将所需的资产从项目窗口拖进Addressables window
的资产组。
指定地址
资产的 默认地址 是项目中资产的路径(例如,Assets/images/myImage.png
)。要从Addressables window
更改资产的地址,请右键单击该资源,然后选择Rename
。
当您第一次开始使用可寻址资产时,系统会在Assets/AddressableAssetsData
文件中为您的项目保存一些编辑时和运行时数据资产,这些资产应添加到您的版本控制签入中。
构建可寻址内容
在构建应用程序之前,Addressables资产系统需要将您的内容构建为正在运行的游戏可以使用的文件。 此步骤不是自动的。您可以通过编辑器或API构建此内容:
- 要在Editor中构建内容,请打开
Addressables window
,然后选择Build
->Build Player Content
。 - 要使用API构建内容,请使用
AddressableAssetSettings.BuildPlayerContent()。
使用可寻址资产
按地址加载或实例化
您可以在运行时加载或实例化可寻址资产。加载资产会将所有依赖项加载到内存中(包括资产的捆绑数据,如果适用),允许您在需要时使用该资产。这实际上并没有将所需资产放入场景中。要将资源添加到场景中,您必须实例化。 使用Addressables实例化接口将加载资源,然后立即将其添加到场景中。
要使用字符串地址从游戏脚本访问资产,请声明UnityEngine.AddressableAssets命名空间,然后调用以下方法:
// 这会使用指定的地址加载资产。
Addressables.LoadAssetAsync<GameObject>("AssetAddress");
// 这会将具有指定地址的资产实例化到场景中。
Addressables.InstantiateAsync("AssetAddress");
注意:
LoadAssetAsync
和InstantiateAsync
是异步操作。您可以在完成加载时提供回调以使用资产。
using UnityEngine.AddressableAssets;
using UnityEngine;
public class AddressablesExample : MonoBehaviour
{
GameObject myGameObject;
// ...
// 使用指定的地址加载资产,完成时回调OnLoadDone方法
Addressables.LoadAssetAsync<GameObject>("AssetAddress").Completed += OnLoadDone;
}
private void OnLoadDone(UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<GameObject> obj)
{
// 在生产环境中,您应该添加异常处理来捕获诸如null结果之类的场景。
myGameObject = obj.Result;
}
}
子资产和组件
子资产
和组件
是资产加载的特殊情况。
组件
:您 无法 直接将GameObject的组件作为资源加载。您必须加载或实例化GameObject,然后从中检索组件引用。子资产
:系统支持加载子资产,但需要特殊语法。潜在子资产的示例包括sprite sheet
中的sprites
或FBX文件中的动画片段。要加载它们,请使用以下示例语法:
// 系统支持加载子资产,但需要特殊语法
// 例如`sprite sheet`中的`sprites`
// FBX文件中的动画片段
Addressables.LoadAssetAsync<IList<Sprite>>("MySpriteSheetAddress");
使用AssetReference类
本AssetReference类
提供一种方法来访问寻址的资产,而无需知道他们的地址。要使用AssetReference类
访问可寻址资产:
- 从
Scene hierarchy
结构或Project window
中选择一个GameObject 。 - 在Inspector中,单击
Add Component
按钮,然后选择组件类型。任何可序列化组件都可以支持AssetReference变量
(例如,a game script
,ScriptableObject
或其他可序列化类
)。 AssetReference
在组件中添加公共变量(例如,public AssetReference explosion;
)。- 在Inspector中,选择要链接到对象的可寻址资产,方法是将资源从
Project window
拖动到公开AssetReference字段中,或者从项目中先前定义的可寻址资产的下拉列表中进行选择(如下所示)。
- 要加载或实例化AssetReference资产,请调用其相应的方法。
AssetRefMember.LoadAssetAsync<GameObject>();
- 或者
AssetRefMember.InstantiateAsync(pos, rot);
建立考虑因素
StreamingAssets中的本地数据
可寻址资产系统在运行时需要 一些文件来知道要加载什么以及如何加载它。这些文件是在构建Addressables数据时生成的,并在StreamingAssets文件夹中生成,该文件夹是Unity中包含构建中所有文件的特殊文件夹。构建Addressables内容时,系统会在库中对这些文件进行分级。然后,在构建应用程序时,系统会将所需文件复制到StreamingAssets,构建并从文件夹中 删除它们。这样,您可以为多个平台构建数据,同时只在每个构建中包含相关数据。
除了特定于Addressables的数据之外,构建其本地数据的任何组也将使用特定于库平台的分段位置。
要验证这是否有效,请将构建路径和加载路径设置为分别以[UnityEngine.AddressableAssets.Addressables.BuildPath]
和开头的配置文件变量{UnityEngine.AddressableAssets.Addressables.RuntimePath}
。您可以在AddressableAssetSettings
的Inspector
中指定这些设置(默认情况下,此对象位于Project的Assets/AddressableAssetsData
目录中)。
提前下载
调用该Addressables.DownloadDependenciesAsync()
方法会加载您传入的地址或标签的依赖关系。通常,这是资产包。
AsyncOperationHandle
此调用返回的结构包含一个PercentComplete
可用于监视和显示下载进度的属性。您还可以让应用程序等到内容加载完毕。
如果您希望在下载之前询问用户是否同意,请使用Addressables.GetDownloadSize()
返回从给定地址或标签下载内容所需的空间。请注意,这会考虑任何以前下载的仍在Unity资产包缓存中的捆绑包。
虽然提前为您的应用程序下载资产可能是有利的,但有些情况下您可能选择不这样做。例如:
- 如果您的应用拥有大量在线内容,并且您通常希望用户只与其中的一部分进行互动。
- 您有一个必须在线连接才能运行的应用程序。如果您的所有应用内容都是小型捆绑包,您可以选择根据需要下载内容。
您可以使用预加载功能显示下载已开始,然后继续,而不是使用百分比完成值等待内容加载。此实现需要加载或等待屏幕来处理资产在需要时尚未完成加载的实例。
构建多个平台
在构建应用程序内容时,可寻址资产系统会生成包含可寻址资产的资产包。资产包依赖于平台,因此必须为您打算支持的每个独特平台重建。
默认情况下,在构建Addressables应用程序数据时,给定平台的数据存储在Addressables构建路径的特定于平台的子目录中。运行时路径考虑了这些平台文件夹,并指向适用的应用程序数据。
注意:如果BuildScriptPackedPlayMode
在编辑器播放模式下使用Addressables 脚本,则Addressables将尝试加载当前活动构建目标(your current active build target)
的数据。因此,如果您当前的构建目标数据与当前的编辑器平台不兼容,则可能会出现问题。 有关更多信息,请参阅有关Play mode scripts.的文档。
开发周期
传统的资产管理
如果在Resources目录中安排内容,它将内置到基本应用程序中,您必须使用该Resources.Load
方法加载内容,并提供资源的路径。
要访问存储在其他位置的内容,您可以使用直接引用或资产包(AB包)。如果您使用资产包(AB包),则会再次按路径加载,将您的负载和组织策略捆绑在一起。如果您的资产包是远程的,或者依赖于其他捆绑包,则必须编写代码来管理下载,加载和卸载所有捆绑包。
可寻址资产管理
为资产提供地址
允许您使用该地址加载它,无论您在项目中的位置或您如何构建资产。您可以毫无问题地更改可寻址资产的路径或文件名。您还可以 将可寻址资产
从Resources文件夹或本地构建目标移动到其他构建位置(包括远程构建位置),而无需更改加载代码。
资产组架构
架构定义了一组数据。您可以在Inspector
中将模式附加到资产组。附加到组的模式集
定义了构建如何处理其内容。例如,在打包模式下构建时,BundledAssetGroupSchema
附加了架构的组充当资产包的源。您可以将模式集合组合到用于定义新组的模板中。您可以通过AddressableAssetsSettings
的Inspector
添加架构模板。
构建脚本
构建脚本ScriptableObject
在项目中表示为实现IDataBuilder接口
的资产。用户可以创建自己的构建脚本,并AddressableAssetSettings
通过其Inspector
将它们添加到对象中。要在“ 可寻址”窗口 Addressables window (Window > Asset Management > Addressables)
,中应用构建脚本,请选择构建脚本
,然后选择下拉选项。目前,有三个脚本用于支持完整的应用程序构建,三个Play模式脚本用于在编辑器中进行迭代。
Play mode scripts
有三个构建脚本,用于创建Play模式数据以帮助您加速应用程序开发。
Fast mode 快速模式
快速模式(BuildScriptFastMode
)允许您在完成游戏流程时快速运行游戏。快速模式直接通过资产数据库加载资产,以便快速迭代,无需创建分析或资产包(AB包)。Virtual mode 虚拟模式
虚拟模式(BuildScriptVirtualMode
)分析布局和依赖关系的内容,而无需创建资产包(AB包)。资产从资产数据库ResourceManager
加载,就像它们是通过捆绑加载一样。要查看在游戏过程中何时加载或卸载软件包,请在Addressable Profiler window (Window > Asset Management > Addressable Profiler).
中查看资产使用情况。
虚拟模式可帮助您模拟加载策略并调整内容组,以便为生产版本找到正确的平衡点。
- Packed Play mode 打包模式
打包播放模式(BuildScriptPackedPlayMode
)使用已构建的资产包(AB包)。此模式与已部署的应用程序构建最匹配,但它要求您将数据构建为单独的步骤。如果您不修改资源,此模式是最快的,因为它在进入播放模式时不会处理任何数据。您必须通过选择Build > Build Player Content
,在Addressables窗口(Window > Asset Management > Addressables)中为此模式 构建内容 或 使用游戏脚本中的方法 AddressableAssetSettings.BuildPlayerContent()
。
- Choosing the right script 选择正确的脚本
要应用播放模式脚本,请从Addressables window menu (Window > Asset Management > Addressables)
, 选择Play Mode Script
, 下拉选项中进行选择.每种模式在开发和部署期间都有自己的时间和地点。下表说明了开发周期的各个阶段,其中特定模式很有用
分析和调试
默认情况下,可寻址资产 仅记录警告和错误。您可以通过打开Player settings window
,然后将ADDRESSABLES_LOG_ALL
添加到Scripting Define Symbols
字段来启用详细日志记录。
您还可以通过在AddressableAssetSettings
取消选中对象Inspector中的Log Runtime Exceptions
选项来禁用异常。可以使用自己的异常处理程序实现该属性ResourceManager.ExceptionHandler
,但这应该在Addressables完成运行时初始化之后完成(见下文)。
初始化对象
您可以将对象附加到可寻址资源设置,并在运行时将它们传递给初始化过程。该CacheInitializationSettings对象
在运行时控制Unity的缓存API. 要创建自己的初始化对象,请创建一个ScriptableObject
并实现该IObjectInitializationDataProvider接口
。这是系统的Editor组件,负责创建ObjectInitializationData
使用运行时数据序列化的组件。
内容更新工作流程
Unity建议将游戏内容分为两类:
- 您永远不会期望更新的静态内容。
- 您希望更新的动态内容。
在此结构中,静态内容随应用程序一起提供(或在安装后很快下载),并且驻留在极少数大型捆绑包中。动态内容在线下载,理想情况下是较小的捆绑包,以最大限度地减少每次更新所需的数据量。可寻址资产系统的目标之一是使这个结构易于使用和修改,而无需更改脚本。
但是,当您不想发布全新的应用程序构建时,可寻址资产系统还可以适应需要更改“静态”内容的情况。
运作流程
Addressables使用内容目录将地址 映射到每个资产,指定加载它的位置和方式。为了使您的应用程序能够修改该映射,您的原始应用程序必须知道此目录的在线副本。要进行此设置,请在AddressableAssetSettings
的Inspector
上启用 Build Remote Catalog(构建远程目录)
设置。这可确保将目录的副本构建到指定路径并从指定路径加载。应用程序发布后,此加载路径无法更改。 内容更新过程创建新版本的目录(具有相同的文件名)以 覆盖先前指定的加载路径上的文件。
构建应用程序会生成 唯一的应用程序内容版本字符串,用于 标识每个应用程序应加载的内容目录. 给定服务器可以包含应用程序的多个版本的目录而不会发生冲突。我们将所需的数据存储在addressables_content_state.bin
文件中。这包括版本字符串
,以及标记为的组中包含的任何资产的哈希信息StaticContent
。默认情况下,此文件与AddressableAssetSettings.asset
文件位于同一文件夹中。
该addressables_content_state.bin
文件包含每个哈希
和依赖信息StaticContent
的 Addressables系统资产组。构建到StreamingAssets文件夹的所有组都应标记为StaticContent
,但大型远程组也可能受益于此指定。在下一节(准备内容更新,如下所述)期间,此哈希信息确定是否有任何StaticContent组包含已更改的资产,因此需要将这些资产移动到其他位置。
准备内容更新
如果您已在任何StaticContent
组中修改了资产,则需要运行Prepare For Content Update
命令。这将从静态组中取出任何已修改的资产,并将它们移动到新组。要生成新资产组:
- 在Unity编辑器中打开Addressables窗口(窗口 > 资产管理 > 可寻址资产)。
- 在“ 可寻址”窗口中,选择顶部菜单栏上的
Build
,然后选择Prepare For Content Update
。 - 在打开的
Build Data File
对话框中,选择addressables_content_state.bin
文件(默认情况下,该文件位于Assets/AddressableAssetsData
项目目录中。
此数据用于确定 自上次构建应用程序以来已修改的资产或依赖项。系统将这些资产移动到新组以准备内容更新构建。
注意:如果所有更改都限制在非静态组中,则此命令将不执行任何操作。
重要:在运行准备操作之前,Unity建议 分支您的版本控制 系统。准备操作以适合更新内容的方式重新排列资产组。分支确保您下次发布新播放器时,可以返回首选内容安排。
构建内容更新
要构建内容更新:
- 在Unity编辑器中打开Addressables窗口(窗口 > 资产管理 > 可寻址资产)。
- 在Addressables窗口中,选择
Build
在顶部菜单上,然后Build For Content Update
。 - 在打开的
Build Data File
对话框中,选择现有应用程序构建的构建文件夹。build文件夹必须包含addressables_content_state.bin
文件。
构建生成content catalog内容目录
,hash file哈希文件
和asset bundles
资产包。
生成的内容目录与所选应用程序构建中的目录具有相同的名称,将 覆盖 旧目录和哈希文件。应用程序加载哈希文件以确定新目录是否可用。系统从应用程序附带或已下载的现有捆绑包中加载未修改的资产。
系统使用addressables_content_state.bin
文件中的内容版本字符串
和位置信息
来创建资产包(AB包)
。不包含更新内容的资产包使用与为更新选择的构建中的文件名相同的文件名编写。如果资产包中包含更新的内容,则会生成一个包含更新内容的新资产包
,并使用新文件名
,以便它可以与原始内容共存。只有具有新文件名的资产包必须复制到托管您内容的位置。
系统还为静态内容构建资产包,但您无需将其上载到内容托管位置,因为没有Addressables资产条目引用它们。
内容更新例子
一个已发布的应用程序知道以下资源组:
玩家的设备上有Local_Static
,并且可能有一个或两个远程包缓存在本地。
如果您修改每个组中的一个资产(AssetA, AssetL, AssetX),然后运行Prepare For Content Update
,那么本地可寻址设置的结果如下:
Prepare For Content Update
操作实际上编辑静态组, 系统构建了上面的布局.
从玩家的角度来看,你最终会得到以下内容:
Local_Static包
已经在设备上,您不能更改它。这个旧版本的AssetA
不再被引用。相反,它作为死数据被卡在设备上.
Remote_Static包
没有改变。如果它还没有缓存在播放器的设备上,它将在请求AssetM
或AssetN
时下载。像AssetA
一样,如果请求AssetL
,这个旧版本的AssetL
不再被引用。
旧的Remote_NonStatic包
被替换为一个新版本,由它的散列文件来区分。修改后的AssetX
版本将使用这个新包进行更新。
content_update_group包
包含修改后的资产组成。
- 任何已更改的本地资产将永久保留在用户设备上.
- 如果用户已经缓存了
Remote_NonStatic
,则需要重新下载包,包括未更改的资产(例如,在本例中,AssetY和AssetZ)。理想情况下,用户没有缓存捆绑包,在这种情况下,他们只需要下载新Remote_NonStatic
捆绑包。 - 如果用户已经缓存了
Static_Remote
捆绑包,则他们只需要下载更新的资产
(在本例中为AssetL
通过content_update_group
)。在这种情况下,这是理想的。如果用户没有缓存包,则必须通过content_update_group
下载新的AssetL
,并通过未触及的Remote_Static
包下载现已失效的资产集。无论初始缓存状态如何,在某种程度上,用户的设备上都将拥有已失效的资产集,尽管从未被访问,但该资产集将被无限期缓存。
远程内容的最佳设置将取决于您的特定用例。
资产托管服务
概观
托管服务提供了一个集成工具,用于使用可寻址资产
配置数据从Unity Editor中将打包内容提供给本地或网络连接的应用程序版本。托管服务旨在提高测试打包内容时的迭代速度,还可用于向本地和远程网络上的连接客户端提供内容。
打包模式测试和迭代
从编辑器播放模式测试转向平台应用程序构建测试会为开发过程带来复杂性和时间成本。托管服务提供可扩展的编辑器嵌入式内容交付服务,可直接映射到您的Addressables组配置。使用自定义Addressables配置文件,您可以快速配置应用程序以从Unity Editor本身加载所有内容。这包括部署到移动设备或任何其他平台的构建,这些构建可以对您的开发系统进行网络访问。
管理key的内容服务器
您可以将资产托管服务部署到服务器环境中,方法是以批处理模式(headless)运行,为内部网和面向internet的Unity应用程序客户机托管内容。
建立
本文详细介绍了项目的资产托管服务的初始设置。虽然设置指南侧重于编辑器工作流,但您可以使用API通过设置类的HostingServicesManager属性
来配置托管服务AddressableAssetSettings。
配置新的主机服务
使用Hosting window
添加,配置和启用新的主机服务。在编辑器中,Window > Asset Management > Hosting Services
,或单击Addressables window
菜单中的Hosting
按钮以访问Hosting window
。
要添加新的主机服务,请单击“ 添加服务”按钮。
在出现的“ 添加服务”对话框中,您可以选择预定义的服务类型或定义自定义服务类型。要使用预定义的服务类型,请从“ 服务类型”下拉选项中进行选择。使用“ 描述性名称”字段输入服务的名称。
新添加的服务显示在“ 主机 ” 窗口的“ 主机服务”部分中,默认为禁用状态。要启动该服务,请单击 启用服务 按钮。
HTTP Hosting Service
在启动时自动分配端口号
。端口号在Unity会话之间保存并重用。要选择其他端口,请在“ 端口”字段中指定特定端口号,或使用“ 重置”按钮随机分配不同的端口。
注意:如果重置端口号,则必须执行完整的应用程序构建以生成并嵌入正确的URL。
现在,HTTP Hosting Service已启用,可以从BuildPath
每个资产组中指定的目录中提供内容。
Profile配置文件设定
在开发期间使用托管服务时,Unity建议创建一个配置文件,该配置文件使用专门为此目的创建的一个或多个目录来配置所有资产组以从托管服务加载内容。
在“ 可寻址”窗口菜单(“ 窗口” >“ 资产管理” >“ 可寻址资源”)中,选择“ 配置文件” >“ 检查配置文件设置”。您也可以通过AddressableAssetSettings
的Inspector
访问这些设置。
接下来,创建一个新的配置文件 在以下示例中,新配置文件称为Editor Hosted
。
修改加载路径字段,改为从托管服务加载。HttpHostingService
是使用本地IP地址和分配给服务的端口的URL。在“ 主机”窗口中,您可以使用命名的配置文件变量PrivateIpAddress
并HostingServicePort
构造URL(例如,http://[PrivateIpAddress]:[HostingServicePort]
)。
此外,您应该修改所有构建路径变量以指向Project的Assets文件夹之外的公共目录
验证是否正确配置了每个组。确保将路径BuildPath
和LoadPath
路径设置为各自的配置文件密钥,这些密钥已修改为与托管服务一起使用。在此示例中,您可以看到如何LoadPath扩展配置文件变量以构建从托管服务加载的正确基本URL。
最后,从Addressables窗口
中选择新配置文件(new profile)
,创建构建并部署到目标设备。Unity Editor现在通过HttpHostingService
服务提供来自应用程序的所有加载请求。您现在可以在不重新部署的情况下对内容进行添加和更改。重建可寻址内容,然后重新启动已部署的应用程序以刷新内容。
从批处理模式中开启服务
您还可以使用托管服务从以批处理模式运行的Unity编辑器中提供内容。为此,请使用以下选项从命令行启动Unity:
-batchMode -executeMethod UnityEditor.AddressableAssets.HostingServicesManager.BatchMode
这将从默认AddressableAssetSettings对象
加载Hosting Services
配置,并启动所有已配置的服务。
要使用备用AddressableAssetSettings
配置,请创建自己的静态方法入口点,以调用UnityEditor.AddressableAssets.HostingServicesManager.BatchMode(AddressableAssetSettings settings)
重载。
实现自定义服务
实现IHostingService
接口的脚本,您的自定义服务将显示在“ 服务类型”下拉选项中。
内存管理
镜像加载和卸载
使用可寻址资产时,确保正确内存管理的主要方法是正确镜像加载和卸载调用。如何执行此操作取决于您的资产类型和加载方法。然而,在所有情况下,release方法要么接受加载的资产,要么接受加载返回的操作句柄。
例如: 加载 a
由AsyncOperationHandle<SceneInstance>
,您可以通过这个返回的句柄释放它,或者通过handle.Result
释放它。Result(在本例中是SceneInstance)。
资产加载
要加载资产,请使用:
Addressables.LoadAssetAsync
Addressables.LoadAssetsAsync
。
这会将资产加载到内存中而不进行实例化
Every time the load call executes, it adds one to the ref-count for each asset loaded. 每次加载调用执行时,它会为每个加载的资产的ref-count添加一个。
如果用相同的地址调用LoadAssetAsync三次,你会得到AsyncOperationHandle结构体
的三个 不同实例,都引用相同的底层操作。对于相应的资产,该操作的ref-count
为3。如果加载成功,则生成的AsyncOperationHandle结构
包含.Result属性
中的资产.您可以使用已加载的资产使用Unity的内置实例化方法进行实例化,该方法不会增加Addressables引用计数。
要卸载资产,请使用Addressables.Release
减少ref-count
的方法。当给定资产的引用计数为零时,该资产已准备好卸载,并减少任何依赖项的引用计数。
注意:资产可能会立即卸载,也可能不会立即卸载,具体取决于现有的依赖项。
场景加载
要加载场景,请使用:
Addressables.LoadSceneAsync
您可以使用此方法加载场景Single模式
,关闭所有打开的场景或Additive模式
.
要卸载场景,请使用:
Addressables.UnloadSceneAsync
或在Single模式下打开新场景。
您可以使用Addressables界面或使用SceneManager.LoadScene
或SceneManager.LoadSceneAsync
方法打开新场景。打开一个新场景将关闭当前场景,正确地减少ref-count
.
GameObject实例化
要加载和实例化GameObject资产,请使用:
Addressables.InstantiateAsync
。
这将实例化由指定location参数
定位的预制件。Addressables系统将加载Prefab及其依赖项,增加所有相关资产的引用计数。
InstantiateAsync
在同一地址上调用三次会导致所有依赖资产的ref-count
为3。
不像调用LoadAssetAsync
三次,每次InstantiateAsync
调用都会返回一个AsyncOperationHandle指向唯一操作的调用。这是因为每个实例化InstantiateAsync
的结果都是唯一的实例。InstantiateAsync
和其他加载调用之间的另一个区别是可选trackHandle
参数。设置为false
,必须AsyncOperationHandle
在释放实例时保持使用。这更有效,但需要更多的开发工作。
要销毁实例化的gameObject,请使用:
Addressables.ReleaseInstance
或关闭包含实例化对象的Scene。
此场景可以在Additive或Single模式下加载(并因此关闭),也可以使用Addressables
或SceneManagement
的API 加载此场景。如果设置trackHandle为false,则只能Addressables.ReleaseInstance
使用句柄调用,而不能使用实际的GameObject 调用。
注意:如果您调用Addressables.ReleaseInstance
的是未使用Addressables
的API 创建的实例,或者是使用该API创建的trackHandle==false,则系统会检测到该false实例并返回以指示该方法无法释放指定的实例。在这种情况下,实例不会被销毁。
Addressables.InstantiateAsync
有一些相关的开销,所以如果你需要每帧数百次实例化相同的对象,考虑通过Addressables
的API 加载,然后通过其他方法实例化。在这种情况下,您将调用Addressables.LoadAssetAsync
,然后保存结果并调用GameObject.Instantiate()
该结果。这允许以 Instantiate同步方式调用。缺点是 Addressables系统不知道您创建了多少个实例,如果管理不当,可能会导致内存问题。例如,引用纹理的预制件将不再具有要引用的有效加载纹理,从而导致渲染问题(或更糟)。这些问题可能很难追踪,因为您可能无法立即触发内存卸载.
可寻址的分析器Profiler
使用 Addressable Profiler窗口 监视所有Addressables系统操作的引用计数
。要在编辑器中访问该窗口,请选择 Window > Asset Management > Addressable Profiler
。
要点:要在分析器中查看数据,必须在对象的检查器中启用Send Profiler Events
设置 在AddressableAssetSettings
。
分析器中提供了以下信息:
- 白色垂直线表示发生加载请求的帧。
- 蓝色背景表示当前正在加载资产。
- 图表的绿色部分表示资产的当前引用计数。
何时清除内存
不再引用的资产(由分析器中蓝色部分的末尾表示)并不一定意味着资产已被卸载。常见的适用方案涉及资产包中的多个资产。
- 在一个资产包(stuff材料)中有三个资产(
树
、坦克
和牛
) - 当
树
加载时,分析器会显示一个树
的ref-count
和一个stuff
的ref-count
。 - 当坦克加载时,分析器将显示
树
和坦克
的一个ref-count
,以及stuff
包的两个ref-count
。 - 如果你释放
树
,它的ref-count变为零,蓝色的条就消失了。
在此示例中,此时tree资产实际上未卸载。您可以加载资产包或其部分内容,但 不能部分卸载资产包。 stuff
在bundle
本身完全卸载之前,不会卸载任何资产。此规则的例外是引擎接口Resources.UnloadUnusedAssets
. 在上面的场景中 执行此方法将导致tree卸载。由于Addressables系统无法识别这些事件,因此探查器图仅反映了Addressables引用计数(不完全是内存的内容)。请注意,如果您选择使用Resources.UnloadUnusedAssets
,则操作非常慢,并且只应在不显示任何故障的屏幕上调用(例如加载屏幕)。
异步操作处理
Addressables
API中的几个方法返回一个AsyncOperationHandle结构
。此句柄的主要目的是允许访问操作的状态和结果。在您调用Addressables.Release
或Addressables.ReleaseInstance
执行操作之前,操作的结果是有效的.
操作完成后,AsyncOperationHandle.Status
属性为AsyncOperationStatus.Succeeded
或AsyncOperationStatus.Failed
。如果成功,您可以通过该AsyncOperationHandle.Result
属性访问结果。
您可以定期检查操作状态,也可以使用该AsyncOperationHandle.Complete
事件注册完成的回调。当您不再需要返回的AsyncOperationHandle结构
提供的资产时,您应该使用该方法释放它Addressables.Release
。
Type vs typeless handles(类型与无类型句柄)
大多数Addressables
API方法返回一个通用AsyncOperationHandle<T>结构
,允许AsyncOperationHandle.Completed
事件和AsyncOperationHandle.Result
对象的类型安全。还有一个非泛型AsyncOperationHandle
结构,您可以根据需要在两个句柄之间进行转换。
请注意,如果您尝试将非泛型句柄
强制转换为不正确类型的泛型句柄
,则会发生运行时异常
AsyncOperationHandle<Texture2D> textureHandle = Addressables.LoadAssetAsync<Texture2D>("mytexture");
// 转换AsyncOperationHandle<Texture2D> 成一个 AsyncOperationHandle:
// 泛型Handle转换成非泛型Handle
AsyncOperationHandle nonGenericHandle = textureHandle;
// 转换AsyncOperationHandle成一个 AsyncOperationHandle<Texture2D>:
// 非泛型Handle转换成泛型Handle
AsyncOperationHandle<Texture2D> textureHandle2 = nonGenericHandle.Convert<Texture2D>();
// 会抛出异常,必须是Texture2D类型
AsyncOperationHandle<Texture> textureHandle3 = nonGenericHandle.Convert<Texture>();
AsyncOperationHandle用例示例
使用AsyncOperationHandle.Completed
回调注册完成事件的侦听器:
void Start()
{
// 注册回调, 加载完成后调用TextureHandle_Completed
AsyncOperationHandle<Texture2D> textureHandle = Addressables.LoadAsset<Texture2D>("mytexture");
textureHandle.Completed += TextureHandle_Completed;
}
private void TextureHandle_Completed(AsyncOperationHandle<Texture2D> handle)
{
if (handle.Status == AsyncOperationStatus.Succeeded)
{
Texture2D result = handle.Result;
// 纹理可以使用了。
}
}
AsyncOperationHandle
实现IEnumerator
所以它可以在协同程序中产生:
public IEnumerator Start()
{
AsyncOperationHandle<Texture2D> handle = Addressables.LoadAssetAsync<Texture2D>("mytexture");
yield return handle;
if (handle.Status == AsyncOperationStatus.Succeeded)
{
Texture2D texture = handle.Result;
// The texture is ready for use.
// ...
// Release the asset after its use:
Addressables.Release(handle);
}
}
Addressables
还通过AsyncOperationHandle.Task
属性支持异步await
。
public async Start()
{
AsyncOperationHandle<Texture2D> handle = Addressables.LoadAssetAsync<Texture2D>("mytexture");
await handle.Task;
// 任务完成了。在存储结果之前,请确保检查状态是否成功。
}
注意:使用SceneManager.LoadSceneAsync
加载场景并将allowSceneActivation
设置为false
或使用Addressables.LoadSceneAsync
并为activateOnLoad
参数传入false
会导致随后的异步操作被阻塞,无法完成。
自定义操作
IResourceProvider
接口 API允许您通过以数据驱动的方式定义位置和依赖项来扩展加载过程。
在某些情况下,您可能希望创建自定义操作。IResourceProvider
API内部构建在这些自定义操作之上。
创建自定义操作
通过从AsyncOperationBase
类派生并覆盖所需的虚拟方法来创建自定义操作。您可以将派生操作传递给ResourceManager.StartOperation
方法以启动操作并接收AsyncOperationHandle结构
。以这种方式启动的操作注册到ResourceManager并出现在Addressables分析器中。
- 执行操作
ResourceManager调用AsyncOperationBase.Execute
方法。以便在可选的依赖项操作完成后进行自定义操作。
- 完成处理
当您的自定义操作完成时,调用AsyncOperationBase.Complete
。在自定义操作对象上。您可以在Execute
方法中调用它,也可以将其推迟到调用的外部。调用AsyncOperationBase.Complete
通知ResourceManager操作已经完成,并将调用关联的AsyncOperationHandle。完成事件。
- 终止操作
ResourceManager调用AsyncOperationBase.Destroy
。释放引用AsyncOperationHandle
时的自定义操作。这里应该释放与自定义操作关联的任何内存或资源。
Analyze分析
Analyze是一个收集有关Projects的Addressables布局信息的工具。在某些情况下,Analyze可能会采取适当的措施来清理项目的状态。在其他情况下,Analyze纯粹是一个信息工具,可让您就Addressables布局做出更明智的决策。
使用Analyze
在编辑器中,打开“ 可寻址”窗口(“ 窗口” >“ 资产管理” >“ 可寻址资源”),然后单击菜单中的Analyze
按钮以打开 Analyze window
.。
“分析”窗口显示“分析”规则列表以及以下操作:
- 分析所选规则
- 分析操作是规则的信息收集步骤。
- 修复所选规则
- 这使用在分析步骤期间收集的数据来执行任何必要的修改并解决问题。
- 清除所选规则
- 此操作将删除分析收集的所有数据并相应地更新TreeView。
提供分析规则
可修复的规则
- 检查重复的组依赖关系
此规则通过扫描所有组BundledAssetGroupSchemas
并投影资产组布局来检查可能重复的资产。这基本上需要触发完整的构建,因此这种检查既耗时又耗费性能。
问题:重复的资产是由共享依赖关系的不同组中的资产导致的,例如共享存在于不同可寻址组中的材质的两个预制体。该材质(及其任何依赖项)将被拉入包含预制块的两组中。为了防止这种情况的发生,必须将材质标记为可寻址的,或者使用预制体之一,或者在它自己的空间中,从而将材料及其依赖关系放在一个单独的可寻址组中。
解决方案:如果此检查发现任何问题,则对此规则运行修复操作会创建一个新的可寻址组,可在其中移动所有相关资产。
例外:如果您有一个包含多个对象的资产,则不同的组可能只会拉入部分资产,而不会实际复制。具有许多网格的FBX就是一个例子。如果一个网格在“GroupA”中而另一个在“GroupB”中,则此规则将认为FBX是共享的,如果运行修复操作,则将其提取到自己的组中。在这种边缘情况下,运行修复操作实际上是有害的,因为两个组都没有完整的FBX资产。
另请注意,重复的资产可能并不总是一个问题。如果资产永远不会被同一组用户请求(例如,特定于区域的资产),则可能需要重复的依赖性,或者至少是无关紧要的。每个项目都是唯一的,因此应根据具体情况评估修复重复的资产依赖性。
无法修复的规则
- 检查Resources找到可寻址的重复依赖项
此规则检测构建的可寻址数据与驻留在Resources文件夹中的资产之间是否存在任何资产或资产依赖关系。
问题:这些重复意味着数据将包含在应用程序构建和Addressables构建中。
解决方案:此规则不可修复,因为不存在适当的操作。它纯粹是信息性的,提醒您冗余。您必须决定如何进行以及采取何种措施(如果有的话)。可能的手动修复的一个示例是将有问题的资产移出Resources文件夹,并使它们成为可寻址的。
- 检查场景是否有可寻址的重复依赖项
此规则检测“编辑器场景”列表和“可寻址”中场景之间共享的任何资产或资产依赖项。
问题:这些重复意味着数据将包含在应用程序构建和Addressables构建中。
解决方案:它纯粹是信息性的,提醒您冗余。您必须决定如何进行以及采取何种措施(如果有的话)。可能的手动修复的一个示例是从构建设置中拉出带有重复引用的内置场景,并使其成为可寻址场景。
将自定义规则添加到GUI
自定义规则必须使用GUI类自身注册AnalyzeWindow.RegisterNewRule
class MyRule : AnalyzeRule {}
[InitializeOnLoad]
class RegisterMyRule
{
static RegisterMyRule()
{
AnalyzeWindow.RegisterNewRule<MyRule>();
}
}
升级到Addressables系统
本文详细介绍了如何修改现有项目以利用可寻址资产。引用资产有三种传统方法:
- 直接引用:将资源直接添加到应用程序自动加载的组件或场景中。
- 资源文件夹:将资源添加到 Resource文件夹并按文件名加载它们。
- Asset Bundles:将资产添加到资产包,然后按文件路径加载它们的依赖项。
直接引用
- 替换对具有资产引用的对象的直接引用(例如,
public GameObject directRefMember;
变为public AssetReference AssetRefMember;
)。 - 将资源拖动到相应组件的Inspector上,就像直接引用一样。
- 如果希望基于对象而不是字符串名称加载资产,请直接从您在设置中创建的AssetReference对象实例化它(例如,
AssetRefMember.LoadAssetAsync<GameObject>();
或AssetRefMember.InstantiateAsync(pos, rot);
)。
注意:可寻址资产系统异步
加载资产。更新对资产引用的直接引用时,还必须更新代码以异步操作
。
Resource文件夹
将Resources文件夹中的资产标记为Addressable时,系统会自动将资源从Resources文件夹移动到名为Resources_moved的Project中的新文件夹。移动资产的默认地址是旧路径,省略文件夹名称。例如,您的加载代码可能会更改Resources.LoadAsync<GameObject>("desert/tank.prefab");
为Addressables.LoadAssetAsync<GameObject>("desert/tank.prefab");
。
AB包方法
当您打开Addressables窗口时,Unity提供将所有资产包转换为可寻址资产组。这是迁移代码的最简单方法。
如果选择手动转换资产,请单击 忽略 按钮。然后,使用先前描述的直接引用或资源文件夹方法。
资产地址的默认路径是其文件路径。如果您使用路径作为资产的地址,则您将以与从捆绑中加载的方式相同的方式加载资产。可寻址资产系统处理捆绑包及其所有依赖项的加载。