本文主要是介绍Unity Adressables 使用说明(六)加载(Load) Addressable Assets,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
【概述】Load Addressable Assets
Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一:
Address
:包含你分配给资源的地址的字符串。Label
:包含分配给一个或多个资源的标签的字符串。AssetReference Object
:AssetReference 的实例。- IResourceLocation 实例:包含加载资源及其依赖项信息的 intermediate object 。
Addressables 如何加载资源
当你调用其中一个资源加载方法时,Addressables 系统开始一个异步操作,执行以下任务:
- 查找指定键的资源位置(不包括
IResourceLocation
键)。 - 收集依赖项列表。
- 下载任何需要的远程 AssetBundles。
- 将 AssetBundles 加载到内存中。
- 将操作的Result 对象设置为加载的对象。
- 更新操作的Status 并调用任何 Completed 事件侦听器。
如果加载操作成功,Status
设置为Succeeded
,可以从Result 对象访问加载的对象或对象。
如果发生错误,异常会被复制到操作对象的 [OperationException](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.OperationException.html#UnityEngine_ResourceManagement_AsyncOperations_AsyncOperationHandle_OperationException)
成员,并且 Status
设置为 Failed
。默认情况下,异常不会作为操作的一部分抛出。但是,你可以为[ResourceManager.ExceptionHandler](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceManager.ExceptionHandler.html#UnityEngine_ResourceManagement_ResourceManager_ExceptionHandler)
属性分配一个处理函数来处理任何异常。你还可以在 Addressable 系统设置中启用[Log Runtime Exceptions](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/manual/AddressableAssetSettings.html)
选项,将错误记录到 Unity Console 。
当你调用加载多个可寻址资源的方法时,可以指定在任何单个加载操作失败时是否中止整个操作,或者是否加载操作能加载的任何资源。在这两种情况下,操作状态都设置为失败。将 releaseDependenciesOnFailure
参数设置为 true
,在调用加载方法时,在任何失败时中止整个操作。
请参考 Operations 获取有关异步操作和在 Unity 脚本中编写异步代码的更多信息。
将加载的资源与其 key 匹配
Unity 加载单个资源的顺序不一定与传递给加载方法的键列表的顺序相同。
如果需要在组合操作中将资源与用于加载它的键关联,可以按照以下步骤执行操作:
- 使用资源键列表加载
[IResourceLocation](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceLocations.IResourceLocation.html)
实例。 - 使用
IResourceLocation
实例作为键加载单个资源。
IResourceLocation
对象包含键信息,因此你可以例如保持一个字典来关联 key 和资源。当你调用加载方法(如 [LoadAssetsAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.LoadAssetsAsync.html)
)时,操作首先查找与键对应的[IResourceLocation](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceLocations.IResourceLocation.html)
实例,然后使用它加载资源。当你使用 IResourceLocation
加载资源时,操作跳过第一步,因此分两步执行操作不会增加显著的额外工作。
下面的示例加载一组键的资源,并按其地址([PrimaryKey](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceLocations.IResourceLocation.PrimaryKey.html#UnityEngine_ResourceManagement_ResourceLocations_IResourceLocation_PrimaryKey)
)将它们插入到字典中。示例首先加载指定键的资源位置。完成该操作后,它为每个位置加载资源,并使用Completed
事件将单个操作句柄插入到字典中。操作句柄可以用于实例化资源,当不再需要资源时,可以释放它们。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Events;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;internal class LoadWithLocation : MonoBehaviour
{public Dictionary<string, AsyncOperationHandle<GameObject>> operationDictionary;public List<string> keys;public UnityEvent Ready;IEnumerator LoadAndAssociateResultWithKey(IList<string> keys){if (operationDictionary == null)operationDictionary = new Dictionary<string, AsyncOperationHandle<GameObject>>();AsyncOperationHandle<IList<IResourceLocation>> locations= Addressables.LoadResourceLocationsAsync(keys,Addressables.MergeMode.Union, typeof(GameObject));yield return locations;var loadOps = new List<AsyncOperationHandle>(locations.Result.Count);foreach (IResourceLocation location in locations.Result){AsyncOperationHandle<GameObject> handle =Addressables.LoadAssetAsync<GameObject>(location);handle.Completed += obj => operationDictionary.Add(location.PrimaryKey, obj);loadOps.Add(handle);}yield return Addressables.ResourceManager.CreateGenericGroupOperation(loadOps, true);Ready.Invoke();}void Start(){Ready.AddListener(OnAssetsReady);StartCoroutine(LoadAndAssociateResultWithKey(keys));}private void OnAssetsReady(){float x = 0, z = 0;foreach (var item in operationDictionary){Debug.Log($"{item.Key} = {item.Value.Result.name}");Instantiate(item.Value.Result,new Vector3(x++ * 2.0f, 0, z * 2.0f),Quaternion.identity, transform);if (x > 9){x = 0;z++;}}}private void OnDestroy(){foreach (var item in operationDictionary){item.Value.Release();}}
}
加载方法使用 ResourceManager.CreateGenericGroupOperation
创建一个组操作。这允许方法在所有加载操作完成后继续。在这种情况下,该方法调度一个 Ready
事件,以通知其他脚本已加载的数据可以使用。
异步加载(Asynchronous loading)
Addressables 系统 API 是异步的,并返回一个[AsyncOperationHandle](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.html)
用于管理操作进度和完成状态。
Addressables 设计为内容位置无关。内容可能需要首先下载,或者使用其他可能需要较长时间的方法。要强制同步执行,请参考 Synchronous Addressables 了解更多信息。
首次加载资源时,handle 至少在一帧后完成。如果内容已经加载,不同的异步加载选项的执行时间可能有所不同。你可以如下等待加载完成:
- Coroutine:在继续执行之前始终至少延迟一帧。
[Completed](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Completed.html)
callback:如果内容尚未加载,则至少延迟一帧,否则回调在同一帧中调用。- 等待
[AsyncOperationHandle.Task](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Task.html#UnityEngine_ResourceManagement_AsyncOperations_AsyncOperationHandle_Task)
:如果内容尚未加载,则至少延迟一帧,否则在同一帧中继续执行。
using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;internal class AsynchronousLoading : MonoBehaviour
{private string address = "tree";private AsyncOperationHandle loadHandle;// 始终至少延迟 1 帧IEnumerator LoadAssetCoroutine(){loadHandle = Addressables.LoadAssetAsync<GameObject>(address);yield return loadHandle;}// 对于新资产加载至少延迟 1 帧// 已加载资产在当前帧调用回调void LoadAssetCallback(){loadHandle = Addressables.LoadAssetAsync<GameObject>(address);loadHandle.Completed += h =>{// 此处加载};}// 对于新资产加载至少延迟 1 帧// 已加载资产在当前帧完成 awaitasync void LoadAssetWait(){loadHandle = Addressables.LoadAssetAsync<GameObject>(address);await loadHandle.Task;}private void OnDestroy(){loadHandle.Release();}
}
Asynchronous Operation Handles
在 Addressables 中,许多任务需要加载或下载信息,然后才能返回结果。为了避免阻塞程序执行,Addressables 将这些任务实现为异步操作。
与同步操作不同,异步操作几乎立即将控制返回给调用方法。然而,结果可能需要一段时间才能可用。
当你调用如 LoadAssetAsync
方法时,它不会直接返回加载的 asset 。相反,它返回一个 AsyncOperationHandle
对象,你可以使用它在 asset 可用时访问它们。
你可以使用以下技术来等待异步操作的结果,同时允许其他脚本继续处理:
- Coroutines and
[IEnumerator](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/manual/AddressableAssetsAsyncOperationHandle.html#coroutine-and-ienumerator-operation-handling)
loops - Event based operation handling
- Task based operation handling
你可以阻塞当前线程等待异步操作完成。但这样做可能会引入性能问题和帧率卡顿。有关详细信息,请参考 Using operations synchronously 。
释放 AsyncOperationHandle
实例
像 LoadAssetsAsync
这样的方法会返回 AsyncOperationHandle
实例,提供操作结果及释放结果和操作对象本身的方法。
只要你想使用结果,就必须保留句柄对象。根据情况,这可能是一帧,直到关卡结束,甚至是应用程序的生命周期。使用 [Addressables.Release](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.Release.html)
方法释放操作句柄及任何相关的 Addressable 资源。
释放操作句柄会递减操作加载的任何资产的引用计数,并使操作句柄对象本身失效。有关引用计数的更多信息,请参考 Memory management 。
如果你在操作的结果范围之外不需要使用它们,可以立即释放句柄。一些 Addressables 方法(如 [UnloadSceneAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.UnloadSceneAsync.html)
)允许你在操作完成后自动释放操作句柄。
如果操作失败,你仍然应该释放操作句柄。Addressables 会释放失败操作期间加载的任何资产,但释放句柄仍然会清除句柄的实例数据。某些加载多个资产的方法(如 LoadAssetsAsync
)允许你选择保留已加载的资产,或者在加载操作的任何部分失败时失败并释放所有内容。
Coroutine 和 IEnumerator Operation Handling
[AsyncOperationHandle](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.html)
实现了 [IEnumerator](https://learn.microsoft.com/dotnet/api/system.collections.ienumerator)
接口,并在操作完成前继续迭代。
在协程中,你可以 yield
操作句柄等待下一次迭代。当完成时,执行流继续到以下语句。你可以将 MonoBehaviour
的 Start
方法实现为协程,这是一个让游戏对象加载和实例化它需要的资产的好方法。
以下脚本使用协程在 Start
方法中加载预制件作为其游戏对象的子对象。它在操作完成前 yield``AsyncOperationHandle
,然后使用相同的句柄实例化预制件。
using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;internal class LoadWithIEnumerator : MonoBehaviour
{public string address;AsyncOperationHandle<GameObject> opHandle;public IEnumerator Start(){opHandle = Addressables.LoadAssetAsync<GameObject>(address);// 如果已经完成,yield 仍然等待到下一帧,所以如果完成就不要 yield。if (!opHandle.IsDone)yield return opHandle;if (opHandle.Status == AsyncOperationStatus.Succeeded){Instantiate(opHandle.Result, transform);}else{opHandle.Release();}}void OnDestroy(){opHandle.Release();}
}
一旦开始,你无法取消 Addressables.LoadAssetsAsync
。然而,在加载完成之前释放句柄会递减句柄的引用计数,并在加载完成时自动释放它。
协程中的组操作
要在继续游戏逻辑的下一步之前执行多个操作(例如在开始关卡前加载预制件和其他 asset),你可以将它们与单个 [Addressables.LoadAssetsAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.LoadAssetsAsync.html)
方法调用结合起来(如果所有操作都加载 asset)。
该方法的 AsyncOperationHandle
与 LoadAssetAsync
相同。你可以在协程中 yield 句柄等待所有操作加载完毕。你也可以传递一个回调方法给 LoadAssetsAsync
,当操作完成加载特定资产时调用该方法。有关示例,请参考 Loading multiple assets 。
你也可以使用[ResourceManager.CreateGenericGroupOperation](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceManager.CreateGenericGroupOperation.html)
创建一个组操作,当其所有成员完成时,该操作也完成。
基于事件的操作处理
你可以将委托函数添加到[AsyncOperationHandle](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.html)
的[Completed](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Completed.html)
事件。当操作完成时,该操作会调用委托函数。
以下脚本与 coroutine and IEnumerator operation handling 示例执行相同的功能,但使用事件委托而不是协程:
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;internal class LoadWithEvent : MonoBehaviour
{public string address;AsyncOperationHandle<GameObject> opHandle;void Start(){// 创建操作opHandle = Addressables.LoadAssetAsync<GameObject>(address);// 添加事件处理程序opHandle.Completed += Operation_Completed;}private void Operation_Completed(AsyncOperationHandle<GameObject> obj){if (obj.Status == AsyncOperationStatus.Succeeded){Instantiate(obj.Result, transform);}else{obj.Release();}}void OnDestroy(){opHandle.Release();}
}
传递给事件委托的句柄实例与原始方法调用返回的句柄相同。你可以使用任一来访问操作的结果和状态,并释放操作句柄和加载的资产。
Task-based operation handling
[AsyncOperationHandle](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.html)
提供了一个[Task](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Task.html#UnityEngine_ResourceManagement_AsyncOperations_AsyncOperationHandle_Task)
对象,你可以与 C# 的 async
和 await
关键字一起使用,以便顺序调用异步方法并处理结果。
以下示例使用一组 key 加载 Addressable asset 。基于任务的方法与协程或基于事件的方法之间的区别在于调用方法的签名。此方法必须包含 async
关键字,并使用 await
关键字与操作句柄的 Task
属性。调用方法(在此示例中为 Start
)在任务完成时暂停操作。然后恢复执行,示例在网格模式下实例化所有加载的预制件。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;internal class LoadWithTask : MonoBehaviour
{// 要加载的标签或地址字符串public List<string> keys = new List<string>() {"characters", "animals"};// 用于加载和释放资产的操作句柄AsyncOperationHandle<IList<GameObject>> loadHandle;public async void Start(){loadHandle = Addressables.LoadAssetsAsync<GameObject>(keys, // 单个键或键列表addressable =>{// 为每个加载的资产调用Debug.Log(addressable.name);}, Addressables.MergeMode.Union, // 如何组合多个标签false); // 是否在任何资产加载失败时失败// 等待操作在后台完成await loadHandle.Task;// 实例化结果float x = 0, z = 0;foreach (var addressable in loadHandle.Result){if (addressable != null){Instantiate<GameObject>(addressable,new Vector3(x++ * 2.0f, 0, z * 2.0f),Quaternion.identity,transform); // 使其成为此对象的子对象if (x > 9){x = 0;z++;}}}}private void OnDestroy(){loadHandle.Release();// 释放与 loadHandle 相关的所有加载资产// 请注意,如果你没有使加载的 Addressable 成为此对象的子对象,// 那么你需要想出另一种方法在所有单个 Addressable 被销毁时释放句柄。}
}
使用基于任务的操作处理时,你可以使用 C# 的 Task
类方法(如 WhenAll
)来控制你希望并行运行的操作以及希望按顺序运行的操作。以下示例说明了如何等待多个操作完成,然后再进行下一任务:
// 加载预制件
var prefabOpHandle = Addressables.LoadAssetsAsync<GameObject>(keys, null, Addressables.MergeMode.Union, false);// 以附加方式加载场景
var sceneOpHandle = Addressables.LoadSceneAsync(nextScene,UnityEngine.SceneManagement.LoadSceneMode.Additive);await System.Threading.Tasks.Task.WhenAll(prefabOpHandle.Task, sceneOpHandle.Task);
使用同步操作(Use operations synchronously)
你可以通过调用操作的 WaitForCompletion
方法来等待操作完成,而无需使用 yield
、等待事件或 async
/await
。此方法会阻塞当前程序执行线程,直到操作完成后再继续当前作用域中的代码执行。
避免在需要大量时间的操作(例如需要下载数据的操作)上调用 WaitForCompletion
,因为调用 WaitForCompletion
可能会导致帧率卡顿并中断 UI 响应。
以下示例通过地址加载一个预制件资源,等待操作完成,然后实例化预制件:
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;internal class LoadSynchronously : MonoBehaviour
{public string address;AsyncOperationHandle<GameObject> opHandle;void Start(){opHandle = Addressables.LoadAssetAsync<GameObject>(address);opHandle.WaitForCompletion(); // 操作完成时返回if (opHandle.Status == AsyncOperationStatus.Succeeded){Instantiate(opHandle.Result, transform);}else{opHandle.Release();}}void OnDestroy(){opHandle.Release();}
}
自定义操作
要创建自定义操作,继承 [AsyncOperationBase](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase-1.html)
类并重写其虚方法。
你可以将派生操作传递给 [ResourceManager.StartOperation](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceManager.StartOperation.html)
方法来启动操作并接收 [AsyncOperationHandle](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.html)
结构体。[ResourceManager](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceManager.html)
会注册以这种方式启动的操作。
执行自定义操作
[ResourceManager](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceManager.html)
在可选的依赖操作完成后调用自定义操作的 [AsyncOperationBase.Execute](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase-1.Execute.html)
方法。
完成处理
自定义操作完成时,调用自定义操作对象的 [AsyncOperationBase.Complete](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase-1.Complete.html)
。你可以在 [Execute](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase-1.Execute.html)
方法中调用它,也可以在调用外部推迟调用。AsyncOperationBase.Complete
会通知 ResourceManager
操作已完成。ResourceManager
会为相关自定义操作实例调用关联的 [AsyncOperationHandle.Completed](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Completed.html)
事件。
终止自定义操作
当操作的[AsyncOperationBase.ReferenceCount](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase-1.DecrementReferenceCount.html)
归零时,ResourceManager
会调用自定义操作的 [AsyncOperationBase.Destroy](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase-1.Destroy.html)
方法。释放引用 [AsyncOperationHandle](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.html)
时,AsyncOperationBase.ReferenceCount
会减少,使用 [Addressables.Release](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.Release.html)
或当自定义操作内部调用 [AsyncOperationBase.DecrementReferenceCount](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase-1.DecrementReferenceCount.html)
时。AsyncOperationBase.Destroy
是你应释放任何与自定义操作相关的内存或资源的地方。
类型和无类型 Operation Handles
大多数启动操作的Addressables
方法返回一个泛型[AsyncOperationHandle<T>](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle-1.html)
结构体,允许操作 [AsyncOperationHandle.Completed](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Completed.html)
事件和[AsyncOperationHandle.Result](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Result.html#UnityEngine_ResourceManagement_AsyncOperations_AsyncOperationHandle_Result)
对象的类型安全。你也可以使用非泛型 [AsyncOperationHandle](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.html)
结构,并根据需要在两种句柄类型之间进行转换。
如果尝试将非泛型句柄转换为错误类型的泛型句柄,会发生运行时异常。例如:
// 使用类型句柄加载资源:
AsyncOperationHandle<Texture2D> textureHandle = Addressables.LoadAssetAsync<Texture2D>("mytexture");// 将 AsyncOperationHandle<Texture2D> 转换为 AsyncOperationHandle:
AsyncOperationHandle nonGenericHandle = textureHandle;// 将 AsyncOperationHandle 转换为 AsyncOperationHandle<Texture2D>:
AsyncOperationHandle<Texture2D> textureHandle2 = nonGenericHandle.Convert<Texture2D>();// 这会抛出异常,因为需要 Texture2D:
AsyncOperationHandle<Texture> textureHandle3 = nonGenericHandle.Convert<Texture>();
报告操作进度
AsyncOperationHandle
提供以下方法,用于监控和报告操作的进度:
[GetDownloadStatus](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.GetDownloadStatus.html)
: 返回一个[DownloadStatus](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.DownloadStatus.html)
结构。该结构包含有关已下载字节数和仍需下载的字节数的信息。[DownloadStatus.Percent](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.DownloadStatus.Percent.html#UnityEngine_ResourceManagement_AsyncOperations_DownloadStatus_Percent)
报告已下载字节的百分比。[AsyncOperationHandle.PercentComplete](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.PercentComplete.html#UnityEngine_ResourceManagement_AsyncOperations_AsyncOperationHandle_PercentComplete)
: 返回所有子操作完成的平均百分比。例如,如果一个操作有五个子操作,每个子操作代表总数的 20%。该值不考虑各个子操作需要下载的数据量。
例如,如果你调用 [Addressables.DownloadDependenciesAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.DownloadDependenciesAsync.html)
并且需要下载五个 AssetBundles,GetDownloadStatus
会告诉你所有子操作总字节数的百分比。PercentComplete
告诉你完成的操作数量的百分比,而不考虑它们的大小。
如果你调用[LoadAssetAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.LoadAssetAsync.html)
,并且在加载资产之前需要下载一个 bundle,下载百分比可能会产生误导。因为在操作完成之前 GetDownloadStatus
的值已达到 100%,而此时操作还有其他子操作要进行。当下载子操作完成时 PercentComplete
的值为 50%,当实际加载到内存中完成时,值为 100%。
同步加载(Synchronous loading)
同步 Addressables API 帮助镜像 Unity 资源加载工作流。AsyncOperationHandles
有一个名为 WaitForCompletion()
的方法,可以强制 asynchronous operation 完成并返回操作的result
。
WaitForCompletion
的result
是它调用的异步操作的结果。如果操作失败,则返回default(TObject)
。
当操作没有失败时,你也可能获得 default(TObject)
结果。这种情况适用于自动在完成时释放其 AsyncOperationHandle
实例的异步操作。Addressables.InitializeAsync
和任何带有 autoReleaseHandle
参数设置为 true
的 API 都会返回 default(TObject)
,即使操作成功。
性能考虑
与直接调用 Resources.Load
或 Instantiate
相比,调用 WaitForCompletion
可能会对运行时性能产生影响。如果 AssetBundle 是本地的或已经下载并缓存,这些性能影响很小。
当 WaitForCompletion
在任何资源加载操作上调用时,所有活跃的资源加载操作都将完成,这是因为 Unity 处理异步操作的方式。为了避免意外的停顿,请在已知当前操作数量时使用WaitForCompletion
,并希望所有活动操作同步完成。
不要在将要获取和下载远程 AssetBundle 的操作上调用 WaitForCompletion。
同步加载示例
void Start()
{// 强制同步加载 GameObject 的基本用例var op = Addressables.LoadAssetAsync<GameObject>("myGameObjectKey");GameObject go = op.WaitForCompletion();// 执行其他工作...Addressables.Release(op);
}
场景限制导致的死锁
Unity 无法同步完成场景加载。即使 activateOnLoad
设置为 true
,调用[Addressables.LoadSceneAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.LoadSceneAsync.html)
返回的操作上的WaitForCompletion
也不能完全加载场景。它会等待依赖项和资源完成,但场景激活必须异步完成。
这可以通过 sceneHandle
或 SceneInstance
上的 ActivateAsync
的 [AsyncOperation](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/AsyncOperation.html)
来完成:
IEnumerator LoadScene(string myScene)
{var sceneHandle = Addressables.LoadSceneAsync(myScene, LoadSceneMode.Additive);SceneInstance sceneInstance = sceneHandle.WaitForCompletion();yield return sceneInstance.ActivateAsync();// 执行其他工作... 现在场景已完成并集成
}
Unity 不能同步卸载场景。调用WaitForCompletion
来卸载场景不会卸载场景或任何资源,并会在控制台中记录警告。
由于 SceneManager
API 在主线程上的场景集成限制,当调用 WaitForCompletion
加载场景时,可能会锁定 Unity 编辑器或 Player 。这种问题发生在连续加载两个场景时,第二个场景加载请求从其 AsyncOperationHandle
调用了WaitForCompletion
。
场景加载需要额外的帧才能在主线程上完全集成,而**WaitForCompletion**
锁定主线程,因此可能会出现 Addressables 被 SceneManager 通知第一个场景已完全加载,但实际上尚未完成所有操作的情况。在此时,场景已完全加载,但 SceneManager 尝试在主线程上调用 UnloadUnusedAssets
,如果场景以单模式加载。然后,第二个场景加载请求通过 WaitForCompletion 锁定主线程,但由于 SceneManager 需要完成 UnloadUnusedAssets
,无法开始加载下一个场景。
为避免此死锁,要么异步加载连续的场景,要么在场景加载请求之间添加延迟。
另一个问题是在Awake
中异步操作未完全加载场景时调用WaitForCompletion
。这可能会阻塞主线程,阻止其他正在进行的异步操作(如卸载 bundle)完成。为避免此死锁,在 Start
中调用 WaitForCompletion
。
请注意,Addressables 在 SceneManager.sceneUnloaded
中注册了一个回调,卸载任何卸载的 Addressable 场景。如果没有其他场景从 bundle 中加载,这可能触发场景 bundle 卸载。
自定义操作
Addressables 支持自定义 AsyncOperation
实例,它们支持 InvokeWaitForCompletion
的唯一实现。可以重写此方法以实现自定义的同步操作。
自定义操作适用于 ChainOperation
和 GroupsOperation
实例。如果希望同步完成链操作,请使自定义操作实现 InvokeWaitForCompletion
并使用自定义操作创建 ChainOperation
。同样,GroupOperations
非常适合使一组 AsyncOperations
(包括自定义操作)一起完成。
ChainOperation
和 GroupOperation
都有自己的 InvokeWaitForCompletion
实现,依赖于它们所依赖的操作的 InvokeWaitForCompletion
实现。
WebGL 支持
WebGL 不支持 WaitForCompletion
。在 WebGL 上,网络请求加载所有文件。在其他平台上,网络请求在后台线程启动,主线程在等待网络请求完成时处于紧密循环(tight loop)状态。Addressables 使用这种方式处理网络请求时的 WaitForCompletion
。
由于 WebGL 是单线程的,紧密循环会阻塞网络请求,操作永远无法完成。如果网络请求在创建的同一帧完成,则 WaitForCompletion 不会有任何问题。然而,这并不保证。
Load Assets
你可以使用 LoadAssetAsync
或 LoadAssetsAsync
方法在运行时加载一个或多个资源。
加载单个资源
使用 [LoadAssetAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.LoadAssetsAsync.html)
方法加载单个 Addressable 资源,通常以地址作为键:
using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;internal class LoadAddress : MonoBehaviour
{public string key;AsyncOperationHandle<GameObject> opHandle;public IEnumerator Start(){opHandle = Addressables.LoadAssetAsync<GameObject>(key);yield return opHandle;if (opHandle.Status == AsyncOperationStatus.Succeeded){GameObject obj = opHandle.Result;Instantiate(obj, transform);}}void OnDestroy(){opHandle.Release();}
}
你可以在调用 LoadAssetAsync
时使用 label 或其他类型的键,而不仅仅是地址。然而,如果键解析出多个资源,则只会加载找到的第一个资源。例如,如果你使用一个 label 调用此方法,该 label 应用于多个资源,则 Addressables 返回首先找到的那些资源之一。
加载多个资源
使用[LoadAssetsAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.LoadAssetsAsync.html)
方法在一次操作中加载多个 Addressable 资源。使用此方法时,可以指定一个键(如 label )或一组键。
指定多个键时,可以指定合并模式来设置匹配每个键的资源如何组合:
Union
: 包含匹配任何键的资源Intersection
: 包含匹配每个键的资源UseFirst
: 仅包含第一个解析为有效位置的键的资源
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;internal class LoadMultiple : MonoBehaviour
{// 要加载的标签字符串public List<string> keys = new List<string>() {"characters", "animals"};// 用于加载和释放资源的操作句柄AsyncOperationHandle<IList<GameObject>> loadHandle;// 通过标签加载 Addressablespublic IEnumerator Start(){float x = 0, z = 0;loadHandle = Addressables.LoadAssetsAsync<GameObject>(keys,addressable =>{// 每加载一个资源都会调用Instantiate<GameObject>(addressable,new Vector3(x++ * 2.0f, 0, z * 2.0f),Quaternion.identity,transform);if (x > 9){x = 0;z++;}}, Addressables.MergeMode.Union, // 如何组合多个标签 false); // 如果任何资源加载失败是否释放yield return loadHandle;}private void OnDestroy(){loadHandle.Release();// 释放与 loadHandle 关联的所有加载资源// 注意,如果你没有将加载的 Addressables 设为此对象的子对象,// 那么你需要设计另一种方法来在所有单独的 Addressables 被销毁时释放句柄。}
}
要指定如何处理加载错误,请使用releaseDependenciesOnFailure
参数。如果为true
,则在加载任何单个资源时遇到错误时操作失败。操作和已加载的任何资源都会被释放。
如果为 false
,则操作会尽可能加载所有对象,并且不会释放操作。如果操作失败,操作仍然会以 Failed
状态完成。此外,返回的资源列表中会在失败资源本应出现的位置包含 null
值。
当加载必须作为一个集合使用的一组资源时,将 releaseDependenciesOnFailure
设置为 true
。例如,如果你加载游戏关卡的资源,你可能会让整个操作失败,而不是只加载部分所需资源。
根据位置加载资源(Load assets by location)
当你通过地址、标签或AssetReference
加载 Addressable 资源时,Addressables 系统首先会查找资源的位置,并使用[IResourceLocation](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceLocations.IResourceLocation.html)
实例下载所需的 AssetBundles
及其任何依赖项。要执行资源加载操作,先使用 [LoadResourceLocationsAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.LoadResourceLocationsAsync.html)
获取 IResourceLocation
对象,然后使用这些对象作为键来加载或实例化资源。
IResourceLocation
对象包含加载一个或多个资源所需的信息。
LoadResourceLocationsAsync
方法永远不会失败。如果无法将指定的键解析到任何资源的位置,它会返回一个空列表。你可以通过在 type
参数中指定特定类型来限制返回的资源位置的类型。
以下示例加载带有 knight
或 villager
标签的所有资源的位置:
AsyncOperationHandle<IList<IResourceLocation>> handle= Addressables.LoadResourceLocationsAsync(new string[] {"knight", "villager"},Addressables.MergeMode.Union);yield return handle;// ...handle.Release();
加载子对象(sub-objects)的 Location
Unity 在运行时生成SubObjects
的位置,以减小内容目录的大小并提高运行时性能。当你使用带有子对象的资源的键调用[LoadResourceLocationsAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.LoadResourceLocationsAsync.html)
且不指定类型时,该方法会为所有子对象和主对象生成 IResourceLocation
实例。同样,如果你不指定用于指向带有子对象的资源的AssetReference
的子对象,那么系统会为每个子对象生成 IResourceLocation
实例。
例如,如果你加载一个 FBX 资源的位置,地址为 myFBXObject
,你可能会获得三个资源的位置:一个 GameObject
、一个网格和一个材质。如果你在地址中指定类型 myFBXObject[Mesh]
,则只会得到网格对象。你也可以使用 LoadResourceLocationsAsync
方法的 type
参数来指定类型。
加载场景(Load a Scene)
使用 [Addressables.LoadSceneAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.LoadSceneAsync.html)
方法通过地址或其他 Addressable key object 加载 Addressable 场景资源。
Addressables.LoadSceneAsync
内部使用了 Unity 引擎的[SceneManager.LoadSceneAsync](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/SceneManagement.SceneManager.LoadSceneAsync.html)
方法。影响 SceneManager.LoadSceneAsync
行为的 API 也会以相同的方式影响 Addressables.LoadSceneAsync
,例如[Application.backgroundLoadingPriority](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/Application-backgroundLoadingPriority.html)
。
Addressables.LoadSceneAsync
方法的剩余参数对应于 SceneManager.LoadSceneAsync
方法的参数:
loadMode
:是将加载的场景添加到当前场景中,还是卸载并替换当前场景。loadSceneParameters
:包括loadMode
和localPhysicsMode
。用于在加载场景时指定创建 2D 还是 3D physics scene 。activateOnLoad
:是否在场景加载完成后立即激活场景,还是等到调用SceneInstance
对象的[ActivateAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceProviders.SceneInstance.ActivateAsync.html)
方法后再激活。对应于[AsyncOperation.allowSceneActivation](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/AsyncOperation-allowSceneActivation.html)
选项。默认值为true
。priority
:用于加载场景的AsyncOperation
的优先级。对应于[AsyncOperation.priority](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/AsyncOperation-priority.html)
选项。默认值为100
。
以下示例以附加模式(additively)加载场景。加载场景的组件存储操作句柄,并在 parent GameObject 被销毁时使用它来卸载和释放场景:
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;internal class LoadSceneByAddress : MonoBehaviour
{public string key; // address stringprivate AsyncOperationHandle<SceneInstance> loadHandle;void Start(){loadHandle = Addressables.LoadSceneAsync(key, LoadSceneMode.Additive);}void OnDestroy(){Addressables.UnloadSceneAsync(loadHandle);}
}
有关更多示例,请参阅 Addressables 示例库中的 Scene loading project 。
如果你使用 [LoadSceneMode.Single](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/SceneManagement.LoadSceneMode.Single.html)
加载场景,Unity 运行时会卸载当前场景并调用 [Resources.UnloadUnusedAssets](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/Resources.UnloadUnusedAssets.html)
。有关更多信息,请参阅 Releasing Addressable assets 。
在编辑器中,即使场景被打包在不可用的远程包中,并且你将 Play 模式脚本设置为使用现有构建,你也可以始终加载当前项目中的场景。编辑器使用资源数据库加载场景。
在场景中使用 Addressables
如果场景是 Addressable,你可以像使用其他资源一样使用场景中的 Addressable 资源。你可以在场景中放置预制件和其他资源,并将资源分配给组件属性。如果你使用了非 Addressable 资源,则该资源会成为场景的隐式依赖项(implicit dependency),并且在构建内容时,构建系统会将其打包到与场景相同的 AssetBundle 中。Addressable 资源根据它们所在的组打包到各自的 AssetBundle 中。
如果场景不是 Addressable ,那么你直接添加到场景层次结构中的任何 Addressable 资源都会成为隐式依赖项,Unity 会将这些资源的副本包含在内置场景数据(built-in scene data)中,即使它们也存在于 Addressable 组中。对于任何资源,例如分配给场景中 GameObject 上的组件的材质,也是如此。
在自定义组件类中,你可以使用 [AssetReference](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.AssetReference.html)
字段来允许在非 Addressable 场景中分配 Addressable 资源。否则,你可以使用地址和 label 在运行时从脚本中加载资源。无论场景是否为 Addressable,都必须在代码中加载 AssetReference
。
加载 AssetBundles
你可以通过 BundledAssetGroupSchema
类控制 AssetBundles 的加载方式。可以通过脚本 API 或者在AddressablesAssetGroup
inspector 的高级选项中设置这些选项。
使用 UnityWebRequest 加载本地包
Addressables 可以通过两种引擎 API 加载 AssetBundles:UnityWebRequest.GetAssetBundle
和 AssetBundle.LoadFromFileAsync
。默认行为是在 AssetBundle 位于本地存储时使用 AssetBundle.LoadFromFileAsync
,在 AssetBundle 路径是 URL 时使用 UnityWebRequest
。
你可以通过将 BundledAssetGroupSchema.UseUnityWebRequestForLocalBundles
设置为 true
来覆盖此行为。也可以通过 BundledAssetGroupSchema
GUI 设置。
以下是几种适用情况:
- 你正在发布使用 LZMA 压缩的本地 AssetBundles,因为你希望发布的游戏包尽可能小。在这种情况下,你可能希望使用
UnityWebRequest
将这些 AssetBundles 重新压缩为本地磁盘缓存中的 LZ4。 - 你正在发布 Android 游戏,并且你的 APK 包含使用默认 APK 压缩的 AssetBundles。
- 你希望将整个本地 AssetBundle 加载到内存中以避免磁盘查找。如果你使用
UnityWebRequest
并禁用缓存,整个 AssetBundle 文件将被加载到内存缓存中。这会增加你的运行时内存使用,但可能会提高加载性能,因为它消除了初始 AssetBundle 加载后的磁盘查找。
上述前两种情况会导致 AssetBundle 在玩家设备上存在两次(原始表示和缓存表示)。这意味着初始加载(解压缩和复制到缓存)比后续加载(从缓存加载)更慢。
处理下载错误(Handle download errors)
当下载失败时,RemoteProviderException
包含可用于确定如何处理失败的错误。RemoteProviderException
可能是 AsyncOperationHandle.OperationException
或内部异常。如下所示:
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.Exceptions;internal class HandleDownloadError : MonoBehaviour
{private AsyncOperationHandle m_Handle;void LoadAsset(){m_Handle = Addressables.LoadAssetAsync<GameObject>("addressKey");m_Handle.Completed += handle =>{string dlError = GetDownloadError(m_Handle);if (!string.IsNullOrEmpty(dlError)){// 处理错误}};}string GetDownloadError(AsyncOperationHandle fromHandle){if (fromHandle.Status != AsyncOperationStatus.Failed)return null;RemoteProviderException remoteException;System.Exception e = fromHandle.OperationException;while (e != null){remoteException = e as RemoteProviderException;if (remoteException != null)return remoteException.WebRequestResult.Error;e = e.InnerException;}return null;}
}
可能的错误字符串包括:
- “Request aborted”(请求已中止)
- “Unable to write data”(无法写入数据)
- “Malformed URL”(URL 格式错误)
- “Out of memory”(内存不足)
- “No Internet Connection”(没有网络连接)
- “Encountered invalid redirect (missing Location header?)”(遇到无效重定向(缺少位置头?))
- “Cannot modify request at this time”(此时无法修改请求)
- “Unsupported Protocol”(不支持的协议)
- “Destination host has an erroneous SSL certificate”(目标主机有错误的 SSL 证书)
- “Unable to load SSL Cipher for verification”(无法加载 SSL 密码进行验证)
- “SSL CA certificate error”(SSL CA 证书错误)
- “Unrecognized content-encoding”(无法识别的内容编码)
- “Request already transmitted”(请求已发送)
- “Invalid HTTP Method”(无效的 HTTP 方法)
- “Header name contains invalid characters”(头名称包含无效字符)
- “Header value contains invalid characters”(头值包含无效字符)
- “Cannot override system-specified headers”(无法覆盖系统指定的头)
- “Backend Initialization Error”(后台初始化错误)
- “Cannot resolve proxy”(无法解析代理)
- “Cannot resolve destination host”(无法解析目标主机)
- “Cannot connect to destination host”(无法连接到目标主机)
- “Access denied”(访问被拒绝)
- “Generic/unknown HTTP error”(通用/未知 HTTP 错误)
- “Unable to read data”(无法读取数据)
- “Request timeout”(请求超时)
- “Error during HTTP POST transmission”(HTTP POST 传输过程中出错)
- “Unable to complete SSL connection”(无法完成 SSL 连接)
- “Redirect limit exceeded”(重定向限制超出)
- “Received no data in response”(未收到响应数据)
- “Destination host does not support SSL”(目标主机不支持 SSL)
- “Failed to transmit data”(传输数据失败)
- “Failed to receive data”(接收数据失败)
- “Login failed”(登录失败)
- “SSL shutdown failed”(SSL 关闭失败)
- “Redirect limit is invalid”(重定向限制无效)
- “Not implemented”(未实现)
- “Data Processing Error, see Download Handler error”(数据处理错误,请参见下载处理程序错误)
- “Unknown Error”(未知错误)
卸载 Addressable 资源
Addressables 系统使用引用计数来检查资源是否在使用中。这意味着你必须在完成使用后释放每个加载或实例化的资源。有关更多信息,请参考 Memory Management 。
当你卸载一个场景时,它所属的 AssetBundle 也会被卸载。这会卸载与该场景相关的资源,包括从原始场景移动到不同场景的任何 GameObjects。
当 Unity 使用 [LoadSceneMode.Single](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/SceneManagement.LoadSceneMode.Single.html)
模式加载场景时,会自动调用 UnloadUnusedAssets
。为了防止场景及其资源被卸载,可以在希望手动卸载场景之前,保留对场景加载操作句柄的引用。为此,可以对加载操作句柄使用 [ResourceManager.Acquire](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceManager.Acquire.html#UnityEngine_ResourceManagement_ResourceManager_Acquire_UnityEngine_ResourceManagement_AsyncOperations_AsyncOperationHandle_)
。
单独从场景加载的各个 Addressables 及其操作句柄不会被释放。你必须调用 [Addressables.Release](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.Release.html)
来释放这些资源。例外情况是,当你使用 [Addressables.InstantiateAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.InstantiateAsync.html)
并设置 trackHandle
为 true
(默认值)实例化的任何 Addressable 资源会自动释放。
更改资源 URL
你可以通过以下几种方式在运行时修改 Addressables 用于加载资源的 URL:
Static Profile Variables
当你定义 RemoteLoadPath Profile variable 变量时,可以使用静态属性来指定应用加载远程内容(包括目录、目录哈希文件和 AssetBundles)的 URL 的全部或部分。有关在 Profile 变量中指定属性名称的信息,请参考 Profile variable syntax 。
静态属性的值必须在 Addressables 初始化之前设置。初始化后更改值无效。
ID Transform Method
你可以为 [Addressables.ResourceManager](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.ResourceManager.html#UnityEngine_AddressableAssets_Addressables_ResourceManager)
对象的 [InternalIdTransformFunc](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceLocations.IResourceLocation.InternalId.html#UnityEngine_ResourceManagement_ResourceLocations_IResourceLocation_InternalId)
属性分配一个方法,以单独更改 Addressables 加载资源的 URL。必须在相关操作开始之前分配该方法,否则将使用默认 URL。
使用 TransformInternalId
对于远程托管很有用。给定单个 IResourceLocation
,你可以将 ID 转换为运行时指定的服务器。这对于服务器 IP 地址更改或使用不同 URL 提供不同应用资源变体非常有用。
ResourceManager
在查找资源时调用TransformInternalId
方法,将资源的[IResourceLocation](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceLocations.IResourceLocation.html)
实例传递给你的方法。你可以更改IResourceLocation
的[InternalId](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceLocations.IResourceLocation.InternalId.html#UnityEngine_ResourceManagement_ResourceLocations_IResourceLocation_InternalId)
属性,并将修改后的对象返回给 ResourceManager
。
以下示例演示了如何为所有 AssetBundles 的 URL 追加查询字符串:
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.AddressableAssets;static class IDTransformer
{// 实现一个方法来转换 location 的内部 idstatic string MyCustomTransform(IResourceLocation location){if (location.ResourceType == typeof(IAssetBundleResource)&& location.InternalId.StartsWith("http", System.StringComparison.Ordinal))return location.InternalId + "?customQueryTag=customQueryValue";return location.InternalId;}// 用你的自定义方法覆盖 Addressables 转换方法。// 这可以设置为 null 以恢复默认行为。[RuntimeInitializeOnLoadMethod]static void SetInternalIdTransform(){Addressables.InternalIdTransformFunc = MyCustomTransform;}
}
WebRequest Override
你可以为 Addressable 对象的 WebRequestOverride
属性分配一个方法,以单独更改用于下载文件(如 AssetBundle 或目录.json
文件)的 UnityWebRequest
。必须在相关操作开始之前分配该方法,否则将使用默认的 UnityWebRequest
。
ResourceManager 在调用 UnityWebRequest.SendWebRequest
之前调用 WebRequestOverride
,并将下载的 UnityWebRequest
传递给你的方法。
以下示例显示了如何为所有 AssetBundles 和目录的 URL 追加查询字符串:
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.AddressableAssets;internal class WebRequestOverride : MonoBehaviour
{// 注册以覆盖 Addressables 创建的用于下载的 WebRequestsprivate void Start(){Addressables.WebRequestOverride = EditWebRequestURL;}// 覆盖 WebRequest 的 url,传递给该方法的请求是 Addressables 标准使用的请求。private void EditWebRequestURL(UnityWebRequest request){if (request.url.EndsWith(".bundle", StringComparison.OrdinalIgnoreCase))request.url = request.url + "?customQueryTag=customQueryValue";else if (request.url.EndsWith(".json", StringComparison.OrdinalIgnoreCase) || request.url.EndsWith(".hash", StringComparison.OrdinalIgnoreCase))request.url = request.url + "?customQueryTag=customQueryValue";}
}
预加载依赖项(Preload dependencies)
当你远程分发内容时,可以通过在应用需要它们之前预先下载依赖项来提高性能。例如,你可以在游戏首次启动时下载必要的内容,以确保用户在游戏过程中不会等待内容加载。
下载依赖项
使用 [Addressables.DownloadDependenciesAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.DownloadDependenciesAsync.html)
方法,确保加载某个 Addressable key 所需的所有依赖项都已安装在应用的本地内容或下载缓存中:
string key = "assetKey";// 检查下载大小
AsyncOperationHandle<long> getDownloadSize = Addressables.GetDownloadSizeAsync(key);
yield return getDownloadSize;// 如果下载大小大于 0,则下载所有依赖项。
if (getDownloadSize.Result > 0)
{AsyncOperationHandle downloadDependencies = Addressables.DownloadDependenciesAsync(key);yield return downloadDependencies;if(downloadDependencies.Status == AsyncOperationStatus.Failed)Debug.LogError("Failed to download dependencies for " + key);// 我们需要释放下载句柄,以便可以加载资源downloadDependencies.Release();
}
获取进度更新(Get progress updates)
[AsyncOperationHandle](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.html)
实例提供以下方式获取进度更新:
[AsyncOperationHandle.PercentComplete](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.PercentComplete.html#UnityEngine_ResourceManagement_AsyncOperations_AsyncOperationHandle_PercentComplete)
:报告已完成子操作的百分比。例如,如果一个操作使用六个子操作来执行其任务,当其中三个操作完成时,PercentComplete
指示整个操作完成了 50%(不考虑每个操作加载的数据量)。[AsyncOperationHandle.GetDownloadStatus](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.GetDownloadStatus.html#UnityEngine_ResourceManagement_AsyncOperations_AsyncOperationHandle_GetDownloadStatus)
:返回一个[DownloadStatus](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.AsyncOperations.DownloadStatus.html)
结构,报告总下载大小的百分比。例如,如果一个操作有六个子操作,但第一个操作代表总下载大小的 50%,则当第一个操作完成时,GetDownloadStatus
指示操作完成了 50%。
以下示例展示了如何使用 GetDownloadStatus
检查状态并在下载过程中分发进度事件:
using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Events;
using UnityEngine.ResourceManagement.AsyncOperations;internal class PreloadWithProgress : MonoBehaviour
{public string preloadLabel = "preload";public UnityEvent<float> ProgressEvent;public UnityEvent<bool> CompletionEvent;private AsyncOperationHandle downloadHandle;IEnumerator Start(){downloadHandle = Addressables.DownloadDependenciesAsync(preloadLabel, false);float progress = 0;while (downloadHandle.Status == AsyncOperationStatus.None){float percentageComplete = downloadHandle.GetDownloadStatus().Percent;if (percentageComplete > progress * 1.1) // 每 10% 或左右报告一次{progress = percentageComplete; // 更准确的百分比ProgressEvent.Invoke(progress);}yield return null;}CompletionEvent.Invoke(downloadHandle.Status == AsyncOperationStatus.Succeeded);downloadHandle.Release(); // 释放操作句柄}
}
要了解加载一个或多个资源需要下载多少数据,可以调用 [Addressables.GetDownloadSizeAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.GetDownloadSizeAsync.html)
:
AsyncOperationHandle<long> getDownloadSize = Addressables.GetDownloadSizeAsync(key);
已完成操作的 Result
是必须下载的字节数。如果 Addressables 已缓存所有必需的 AssetBundles,则 Result
为零。
在读取 Result
对象后,总是释放下载操作句柄。如果不需要访问下载操作的结果,可以通过将 autoReleaseHandle
参数设置为 true
来自动释放句柄,如以下示例所示:
using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;internal class Preload : MonoBehaviour
{public IEnumerator Start(){yield return Addressables.DownloadDependenciesAsync("preload", true);}
}
清除依赖项缓存(Clear the dependency cache)
如果要清除 Addressables 缓存的任何 AssetBundles,可以调用 [Addressables.ClearDependencyCacheAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.ClearDependencyCacheAsync.html)
。此方法会清除与键标识的资源相关的缓存 AssetBundles 以及包含这些资源依赖项的任何捆绑包。
ClearDependencyCacheAsync
仅清除与指定键相关的资源包。如果更新内容目录使键不再存在或不再依赖于相同的 AssetBundles,那么这些捆绑包将保留在缓存中,直到根据 cache settings 过期。
要清除所有 AssetBundles,可以使用 UnityEngine.Caching 类中的方法。
从多个项目加载内容(Load Content from Multiple Projects)
如果你在处理多个项目,例如一个跨多个 Unity 项目拆分的大型项目,可以使用 [Addressables.LoadContentCatalogAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/manual/LoadContentCatalogAsync.html)
将不同项目中的代码和内容链接在一起。
设置多个项目
要创建多项目设置,请确保以下几点:
- 每个项目使用相同版本的 Unity 编辑器。
- 每个项目使用相同版本的 Addressables 包。
项目可以包含你需要的任何资源和代码。其中一个项目必须是你的主项目(main project)或源项目(source project),这是你构建和部署游戏二进制文件的项目。通常,源项目主要包含代码而几乎没有内容。主要项目中的内容至少包括一个启动场景。你可能希望包括在任何 AssetBundles 下载和缓存之前需要本地化的场景以提高性能。
辅助项目(Secondary projects)则相反,主要包含内容而几乎没有代码。这些项目需要启用所有远程 Addressable 组和构建 Remote Catalog 。内置到这些项目中的任何本地数据不能在源项目的应用程序中加载。非关键场景可以位于这些项目中,并在需要时由主项目下载。
处理多个项目
一旦设置好项目,工作流程通常如下:
- 为所有辅助项目构建远程内容(Build Remote Content)。
- 为源项目构建 Addressables 内容。
- 启动源项目的 Play 模式或构建源项目的二进制文件。
- 在源项目中,使用
[Addressables.LoadContentCatalogAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/manual/LoadContentCatalogAsync.html)
加载其他各种项目的 Remote Catalog 。 - 正常进行游戏运行时操作。现在,目录已加载,Addressables 可以从这些位置加载资源。
建议在源项目中本地构建最少量的内容。每个项目都是独特的,有独特的需求,但建议在出现互联网连接问题或其他各种问题时,拥有一小部分需要的内容以运行游戏。
处理内置(built in)资源和着色器
Addressables 为每一组 Addressables 玩家数据构建一个 Unity 内置资源包。这意味着当加载多个在辅助项目中构建的 AssetBundles 时,可能会同时加载多个内置包。
根据你的具体情况,可能需要在AddressableAssetSettings
对象上使用内置包命名前缀。每个内置包需要与其他项目中构建的内置包命名不同。如果命名没有不同,你会收到以下错误:
The AssetBundle [bundle] can't be loaded because another AssetBundle with the same files is already loaded.
这篇关于Unity Adressables 使用说明(六)加载(Load) Addressable Assets的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!