运行环境
Unity 2020.3.3f1
Addressable 1.16.16
资源引用
AssetReference
资产引用
参考
AssetReference
属性
Asset
加载的资产
AssetReferenceT
泛型资产引用
参考
AssetReferenceT
AssetLabelReference
资产标签引用
用来加载一组带有该标签的资产
参考
AssetLabelReference
AsyncOperationHandle
异步加载返回的类型
属性
Result
异步加载的结果,加载失败时为null
Status : AsyncOperationStatus
- None(进行中)
- Succeeded(成功)
- Failed(失败)
IsDone : bool
Status成功或失败时为 true
PercentComplete : float
完成的百分比(0~1)
Addressables API
InstantiateAsync(异步实例化)
using UnityEngine;
using UnityEngine.AddressableAssets;
public class BasicReference : MonoBehaviour
{
public AssetReference baseCube;
public void SpawnThing()
{
// 使用 Addressable Name 实例化(区分大小写)
Addressables.InstantiateAsync("Assets/Prefabs/Cube.prefab");
// 使用 AssetReference 实例化
Addressables.InstantiateAsync(baseCube);
// 使用 RuntimeKey 实例化
Addressables.InstantiateAsync(baseCube.RuntimeKey);
// 使用 AssetReference实例方法 实例化
baseCube.InstantiateAsync();
}
}
注意点
ReleaseInstance(释放实例)
using UnityEngine;
using UnityEngine.AddressableAssets;
public class SelfDestruct : MonoBehaviour
{
public float lifetime = 2f;
private void Start()
{
Invoke(nameof(Release), lifetime);
}
private void Release()
{
// 释放实例
if (!Addressables.ReleaseInstance(gameObject))
{
Destroy(gameObject);
}
}
}
注意点
- 用 Addressables 实例化的对象,使用 Addressables.ReleaseInstance 释放实例
- 用 Instantiate 实例化的对象,使用 Destroy 销毁对象
LoadAssetAsync(异步加载资产)
轮询
using System;
using UnityEngine;
using UnityEngine.AddressableAssets;
public class FilteredReferences : MonoBehaviour
{
[Serializable]
public class AssetReferenceMaterial : AssetReferenceT<Material>
{
public AssetReferenceMaterial(string guid) : base(guid)
{
}
}
public AssetReferenceGameObject leftObject;
public AssetReferenceGameObject rightObject;
public AssetReferenceMaterial spawnMaterial;
public AssetReferenceMaterial midMaterial;
public AssetReferenceMaterial lateMaterial;
public Vector3 leftPosition;
public Vector3 rightPosition;
private MeshRenderer m_LeftMeshRender;
private MeshRenderer m_RightMeshRender;
private int m_FrameCounter = 0;
private void Start()
{
leftObject.LoadAssetAsync();
rightObject.LoadAssetAsync();
spawnMaterial.LoadAssetAsync();
midMaterial.LoadAssetAsync();
lateMaterial.LoadAssetAsync();
}
private void FixedUpdate()
{
m_FrameCounter++;
switch (m_FrameCounter)
{
case 20:
{
if (leftObject.Asset)
{
var leftGo = Instantiate(leftObject.Asset, leftPosition, Quaternion.identity) as GameObject;
m_LeftMeshRender = leftGo.GetComponent<MeshRenderer>();
}
if (rightObject.Asset)
{
var rightGo = Instantiate(rightObject.Asset, rightPosition, Quaternion.identity) as GameObject;
m_RightMeshRender = rightGo.GetComponent<MeshRenderer>();
}
if (spawnMaterial.Asset && m_LeftMeshRender != null && m_RightMeshRender != null)
{
m_LeftMeshRender.material = spawnMaterial.Asset as Material;
m_RightMeshRender.material = spawnMaterial.Asset as Material;
}
break;
}
case 40:
{
if (midMaterial.Asset && m_LeftMeshRender != null && m_RightMeshRender != null)
{
m_LeftMeshRender.material = midMaterial.Asset as Material;
m_RightMeshRender.material = midMaterial.Asset as Material;
}
break;
}
case 60:
{
m_FrameCounter = 0;
if (lateMaterial.Asset && m_LeftMeshRender != null && m_RightMeshRender != null)
{
m_LeftMeshRender.material = lateMaterial.Asset as Material;
m_RightMeshRender.material = lateMaterial.Asset as Material;
}
break;
}
}
}
private void OnDisable()
{
// note that this may be dangerous, as we are releasing the asset without knowing if the instances still exist.
// sometimes that's fine, sometimes not.
leftObject.ReleaseAsset();
rightObject.ReleaseAsset();
spawnMaterial.ReleaseAsset();
midMaterial.ReleaseAsset();
lateMaterial.ReleaseAsset();
}
}
回调
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class ListOfReferences : MonoBehaviour
{
public List<AssetReference> shapes;
private bool m_IsReady = false;
private int m_ToLoadCount;
private int currentIndex = 0;
private void Start()
{
m_ToLoadCount = shapes.Count;
foreach (var shape in shapes)
{
shape.LoadAssetAsync<GameObject>().Completed += OnShapeLoaded;
}
}
private void OnShapeLoaded(AsyncOperationHandle<GameObject> obj)
{
m_ToLoadCount--;
if (m_ToLoadCount <= 0)
{
// 资源全部加载完成,准备就绪
m_IsReady = true;
}
}
public void SpawnAThing()
{
if (!m_IsReady)
{
Debug.Log("还未准备就绪");
return;
}
for (int count = 0; count <= currentIndex; count++)
{
Instantiate(shapes[currentIndex].Asset);
}
currentIndex = (currentIndex + 1) % shapes.Count;
}
private void OnDestroy()
{
foreach (var shape in shapes)
{
shape.ReleaseAsset();
}
}
}
LoadAssetsAsync(异步加载多个资产)
使用 AssetLabelReference 加载
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class BasicReference : MonoBehaviour
{
public AssetLabelReference assetLabelReference;
public void SpawnThing()
{
// 使用 AssetLabelReference 加载
Addressables.LoadAssetsAsync<GameObject>(assetLabelReference, OnLoadedAsset)
.Completed += OnCompleted;
// 使用 RuntimeKey 加载
Addressables.LoadAssetsAsync<GameObject>(assetLabelReference.RuntimeKey, OnLoadedAsset)
.Completed += OnCompleted;
}
/// <summary>
/// 全部资产加载完成回调
/// </summary>
/// <param name="handle"></param>
private void OnCompleted(AsyncOperationHandle<IList<GameObject>> handle)
{
foreach (var item in handle.Result)
{
Debug.Log($"实例化: {item}");
Instantiate(item);
}
Addressables.Release(handle);
}
/// <summary>
/// 资产加载完成回调
/// </summary>
/// <param name="obj">加载完成的资产</param>
private void OnLoadedAsset(GameObject obj)
{
Debug.Log($"加载完成: {obj}");
}
}
指定 label 加载
using UnityEngine;
using UnityEngine.AddressableAssets;
public class BasicReference : MonoBehaviour
{
public void SpawnThing()
{
// 使用 label 加载(区分大小写)
Addressables.LoadAssetsAsync<GameObject>("big", null)
.Completed += handle =>
{
foreach (var item in handle.Result)
{
Debug.Log($"实例化: {item}");
Instantiate(item);
}
Addressables.Release(handle);
};
}
}
指定 addressable name 与 label 加载
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
public class BasicReference : MonoBehaviour
{
public void SpawnThing()
{
// var key = new List<string> {"big", "Assets/Prefabs/Cube.prefab"}; // key区分大小写,字符串顺序无所谓
var key = new List<string> {"Assets/Prefabs/Cube.prefab", "big"}; // 顺序调换后依旧可以加载
// 加载所有 (addressable name = "Assets/Prefabs/Cube.prefab", label = "big") 的GameObject类型资产
Addressables.LoadAssetsAsync<GameObject>(key, null, Addressables.MergeMode.Intersection)
.Completed += handle =>
{
foreach (var item in handle.Result)
{
Debug.Log($"实例化: {item}");
Instantiate(item);
}
Addressables.Release(handle);
};
}
}
注意点
LoadSceneAsync(异步加载场景)
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class AddScenes : MonoBehaviour
{
public string addressToAdd;
private Button m_AddButton;
public TMPro.TMP_Text textArea;
private SceneInstance m_LoadedScene;
private bool m_ReadyToLoad = true;
private void Start()
{
m_AddButton = GetComponent<Button>();
m_AddButton.onClick.AddListener(OnButtonClick);
textArea.text = "Load " + addressToAdd;
}
private void OnButtonClick()
{
if (string.IsNullOrEmpty(addressToAdd))
{
Debug.LogError("Address To Add not set.");
}
else
{
if (m_ReadyToLoad)
{
Addressables.LoadSceneAsync(addressToAdd, LoadSceneMode.Additive).Completed += OnSceneLoaded;
}
else
{
Addressables.UnloadSceneAsync(m_LoadedScene).Completed += OnSceneUnloaded;
}
}
}
private void OnSceneUnloaded(AsyncOperationHandle<SceneInstance> obj)
{
if (obj.Status == AsyncOperationStatus.Succeeded)
{
textArea.text = "Reload " + addressToAdd;
m_ReadyToLoad = true;
m_LoadedScene = new SceneInstance();
}
else
{
Debug.LogError("Failed to unload scene at address: " + addressToAdd);
}
}
private void OnSceneLoaded(AsyncOperationHandle<SceneInstance> obj)
{
if (obj.Status == AsyncOperationStatus.Succeeded)
{
textArea.text = "Unload " + addressToAdd;
m_LoadedScene = obj.Result;
m_ReadyToLoad = false;
}
else
{
Debug.LogError("Failed to load scene at address: " + addressToAdd);
}
}
}
热更新
流程
-
编译
-
编译 addressables
-
编译 可执行文件
-
更新
- 更新 addressables中的资产
- 更新 之前编译的addressables
- Addressable Groups界面中,选择 Build -> Update a Previous Build -> [对应的平台]
注意点
- 编译可执行文件之前需要先编译 addressables