Unity Adressables 使用说明(六)加载(Load) Addressable Assets

2024-09-08 03:36

本文主要是介绍Unity Adressables 使用说明(六)加载(Load) Addressable Assets,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【概述】Load Addressable Assets

Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一:

  • Address:包含你分配给资源的地址的字符串。
  • Label:包含分配给一个或多个资源的标签的字符串。
  • AssetReference Object:AssetReference 的实例。
  • IResourceLocation 实例:包含加载资源及其依赖项信息的 intermediate object 。

Addressables 如何加载资源

当你调用其中一个资源加载方法时,Addressables 系统开始一个异步操作,执行以下任务:

  1. 查找指定键的资源位置(不包括IResourceLocation键)。
  2. 收集依赖项列表。
  3. 下载任何需要的远程 AssetBundles。
  4. 将 AssetBundles 加载到内存中。
  5. 将操作的Result 对象设置为加载的对象。
  6. 更新操作的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 加载单个资源的顺序不一定与传递给加载方法的键列表的顺序相同。

如果需要在组合操作中将资源与用于加载它的键关联,可以按照以下步骤执行操作:

  1. 使用资源键列表加载[IResourceLocation](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.ResourceManagement.ResourceLocations.IResourceLocation.html)实例。
  2. 使用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 操作句柄等待下一次迭代。当完成时,执行流继续到以下语句。你可以将 MonoBehaviourStart 方法实现为协程,这是一个让游戏对象加载和实例化它需要的资产的好方法。

以下脚本使用协程在 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)。

该方法的 AsyncOperationHandleLoadAssetAsync 相同。你可以在协程中 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# 的 asyncawait 关键字一起使用,以便顺序调用异步方法并处理结果。

以下示例使用一组 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

WaitForCompletionresult是它调用的异步操作的结果。如果操作失败,则返回default(TObject)

当操作没有失败时,你也可能获得 default(TObject) 结果。这种情况适用于自动在完成时释放其 AsyncOperationHandle 实例的异步操作。Addressables.InitializeAsync和任何带有 autoReleaseHandle 参数设置为 true 的 API 都会返回 default(TObject),即使操作成功。

性能考虑

与直接调用 Resources.LoadInstantiate 相比,调用 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也不能完全加载场景。它会等待依赖项和资源完成,但场景激活必须异步完成。

这可以通过 sceneHandleSceneInstance 上的 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 的唯一实现。可以重写此方法以实现自定义的同步操作。

自定义操作适用于 ChainOperationGroupsOperation 实例。如果希望同步完成链操作,请使自定义操作实现 InvokeWaitForCompletion 并使用自定义操作创建 ChainOperation。同样,GroupOperations 非常适合使一组 AsyncOperations(包括自定义操作)一起完成。

ChainOperationGroupOperation 都有自己的 InvokeWaitForCompletion 实现,依赖于它们所依赖的操作的 InvokeWaitForCompletion 实现。

WebGL 支持

WebGL 不支持 WaitForCompletion。在 WebGL 上,网络请求加载所有文件。在其他平台上,网络请求在后台线程启动,主线程在等待网络请求完成时处于紧密循环(tight loop)状态。Addressables 使用这种方式处理网络请求时的 WaitForCompletion

由于 WebGL 是单线程的,紧密循环会阻塞网络请求,操作永远无法完成。如果网络请求在创建的同一帧完成,则 WaitForCompletion 不会有任何问题。然而,这并不保证。

Load Assets

你可以使用 LoadAssetAsyncLoadAssetsAsync 方法在运行时加载一个或多个资源。

加载单个资源

使用 [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 参数中指定特定类型来限制返回的资源位置的类型。

以下示例加载带有 knightvillager 标签的所有资源的位置:

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:包括 loadModelocalPhysicsMode。用于在加载场景时指定创建 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.GetAssetBundleAssetBundle.LoadFromFileAsync。默认行为是在 AssetBundle 位于本地存储时使用 AssetBundle.LoadFromFileAsync,在 AssetBundle 路径是 URL 时使用 UnityWebRequest

你可以通过将 BundledAssetGroupSchema.UseUnityWebRequestForLocalBundles 设置为 true 来覆盖此行为。也可以通过 BundledAssetGroupSchema GUI 设置。

以下是几种适用情况:

  1. 你正在发布使用 LZMA 压缩的本地 AssetBundles,因为你希望发布的游戏包尽可能小。在这种情况下,你可能希望使用 UnityWebRequest 将这些 AssetBundles 重新压缩为本地磁盘缓存中的 LZ4。
  2. 你正在发布 Android 游戏,并且你的 APK 包含使用默认 APK 压缩的 AssetBundles。
  3. 你希望将整个本地 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) 并设置 trackHandletrue(默认值)实例化的任何 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 。内置到这些项目中的任何本地数据不能在源项目的应用程序中加载。非关键场景可以位于这些项目中,并在需要时由主项目下载。

处理多个项目

一旦设置好项目,工作流程通常如下:

  1. 为所有辅助项目构建远程内容(Build Remote Content)。
  2. 为源项目构建 Addressables 内容。
  3. 启动源项目的 Play 模式或构建源项目的二进制文件。
  4. 在源项目中,使用[Addressables.LoadContentCatalogAsync](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/manual/LoadContentCatalogAsync.html)加载其他各种项目的 Remote Catalog 。
  5. 正常进行游戏运行时操作。现在,目录已加载,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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1146995

相关文章

Zookeeper安装和配置说明

一、Zookeeper的搭建方式 Zookeeper安装方式有三种,单机模式和集群模式以及伪集群模式。 ■ 单机模式:Zookeeper只运行在一台服务器上,适合测试环境; ■ 伪集群模式:就是在一台物理机上运行多个Zookeeper 实例; ■ 集群模式:Zookeeper运行于一个集群上,适合生产环境,这个计算机集群被称为一个“集合体”(ensemble) Zookeeper通过复制来实现

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

【北交大信息所AI-Max2】使用方法

BJTU信息所集群AI_MAX2使用方法 使用的前提是预约到相应的算力卡,拥有登录权限的账号密码,一般为导师组共用一个。 有浏览器、ssh工具就可以。 1.新建集群Terminal 浏览器登陆10.126.62.75 (如果是1集群把75改成66) 交互式开发 执行器选Terminal 密码随便设一个(需记住) 工作空间:私有数据、全部文件 加速器选GeForce_RTX_2080_Ti