U4S4L5——动态加载单个资源
本章代码关键字
1 2 3 Addressables.LoadAssetAsync<>() Addressable.ReleaseAsset() Addressables.LoadSceneAsync()
通过资源名或标签名动态加载单个资源
根据名字或标签加载单个资源相对之前的指定加载资源更加灵活
主要通过Addressables类中的静态方法传入资源名或标签名进行加载
注意:
如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个
释放资源时需要传入之前记录的AsyncOperationHandle
对象
注意:一定要保证资源使用完毕过后再释放资源
场景异步加载可以自己手动激活加载完成的场景
命名空间:UnityEngine.AddressableAssets
和 UnityEngine.ResourceManagement.AsyncOperations
Addressables.LoadAssetAsync<>()
和使用资源引用标识类加载类似,
也是通过返回的AsyncOperationHandle<>
来添加回调,并确认是否加载成功和获取资源
区别在于该方法需要传入名字或者标签(不需要同时传入)
注意:
如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个
假设要加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void Start (){ AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Cube" ); handle.Completed += (handle) => { if (handle.Status == AsyncOperationStatus.Succeeded) { Instantiate(handle.Result); } }; Addressables.LoadAssetAsync<GameObject>("Red" ).Completed += (handle2) => { if (handle2.Status == AsyncOperationStatus.Succeeded) { Instantiate(handle2.Result); } }; }
输出:
释放资源
Addressable.ReleaseAsset()
,注意,请务必在资源加载完毕后才卸载资源,该方法需要传入之前加载资源得到的AsyncOperationHandle<>
和标识类的释放资源不同,不管任何资源,即使是预设体 只要释放后 都会影响之前在使用该资源的对象(只会在真实打包时出现问题)
因此,释放资源必须要在资源完全不使用后才能进行
1 2 3 4 5 6 7 8 9 10 11 12 void Start (){ AsyncOperationHandle<GameObject> handle1 = Addressables.LoadAssetAsync<GameObject>("Cube" ); handle1.Completed += (handle) => { if (handle.Status == AsyncOperationStatus.Succeeded) { Instantiate(handle.Result); } Addressables.Release(handle); }; }
显示效果:
可见,卸载加载出来的预设体后,预设体的材质也一同丢失了
动态加载场景
参数一:场景名
参数二:加载模式(叠加还是单独,叠加就是两个场景一起显示,单独就是只保留新加载的场景,正常情况为单独,可选,默认是单独)
参数三:场景加载是否激活,可选,默认是true
,如果为false
,加载完成后不会直接切换,需要自己使用返回值中的ActivateAsync
方法
参数四:场景加载的异步操作优先级(可选,默认是100)
1 Addressables.LoadSceneAsync("SampleScene" );
如果参数二传入的是UnityEngine.SceneManagement.LoadSceneMode.Additive
,则两个场景就会叠加起来
1 Addressables.LoadSceneAsync("SampleScene" , UnityEngine.SceneManagement.LoadSceneMode.Additive);
如果参数三传入的是true
,则不会自动激活加载出来的场景,会将加载出来的场景会隐藏,等待手动激活
1 Addressables.LoadSceneAsync("SampleScene" , UnityEngine.SceneManagement.LoadSceneMode.Single, false );
如要激活加载出来的场景,需要从AsyncOperationHandle<>.Result
获取场景,并执行ActivateAsync()
(需要在场景加载完毕后执行)
原场景是否移除取决于执行Addressables.LoadSceneAsync()
时第二个参数是否传入了UnityEngine.SceneManagement.LoadSceneMode.Single
1 2 3 4 5 6 7 8 9 10 void Start (){ Addressables.LoadSceneAsync("SampleScene" , UnityEngine.SceneManagement.LoadSceneMode.Single, false ).Completed += (obj) => { obj.Result.ActivateAsync().completed += (obj) => { }; }; }
注意:场景资源也是可以释放的,并不会影响当前已经加载出来的场景(笔者测试发现,在真实打包环境下,卸载场景会导致天空盒材质以及场景上的材质丢失问题,与课上说的情况不一致) ,因为场景的本质只是配置文件
1 2 3 4 5 6 7 8 9 10 11 void Start (){ Addressables.LoadSceneAsync("SampleScene" , UnityEngine.SceneManagement.LoadSceneMode.Single, false ).Completed += (handle) => { handle.Result.ActivateAsync().completed += (scene) => { Addressables.Release(handle); }; }; }
笔者测试发现,在真实打包环境下,卸载场景会导致天空盒材质以及场景上的材质丢失问题,与课上说的情况不一致,原因不明
实现一个Addressable管理器
资源动态加载可以通过返回的对象AsyncOperationHandle<>
(异步操作句柄) 添加完成监听来处理加载后的资源
释放资源时也是通过释放返回的对象AsyncOperationHandle<>
(异步操作句柄) 来进行释放
如果分散在各脚本中自己管理资源难免显得太过凌乱,所以我们可以通过一个资源管理器来管理所有的异步加载返回对象AsyncOperationHandle<>
所以如果我们要自己写一个Addressables
资源管理器,主要就是用来管理AsyncOperationHandle<>
对象的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 using System;using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.AddressableAssets;using UnityEngine.ResourceManagement.AsyncOperations;public class AddressableMgr { private static AddressableMgr instance; public static AddressableMgr Instance => instance ??= new AddressableMgr(); public Dictionary<string , IEnumerator> resDic = new Dictionary<string , IEnumerator>(); private AddressableMgr () { } public void LoadAssetAsync <T >(string name, Action<AsyncOperationHandle<T>> callBack ) { string keyName = name + "_" + typeof (T).Name; AsyncOperationHandle<T> handle; if (resDic.ContainsKey(keyName)) { handle = (AsyncOperationHandle<T>)resDic[keyName]; if (handle.IsDone) { if (handle.Status == AsyncOperationStatus.Succeeded) { callBack?.Invoke(handle); } else { Debug.LogWarning(keyName + "资源加载失败!" ); resDic.Remove(keyName); } } else { handle.Completed += (obj) => { if (obj.Status == AsyncOperationStatus.Succeeded) callBack?.Invoke(obj); else { Debug.LogWarning(keyName + "资源加载失败!" ); if (resDic.ContainsKey(keyName)) resDic.Remove(keyName); } }; } return ; } handle = Addressables.LoadAssetAsync<T>(name); handle.Completed += (obj) => { if (obj.Status == AsyncOperationStatus.Succeeded) callBack?.Invoke(obj); else { Debug.LogWarning(keyName + "资源加载失败!" ); if (resDic.ContainsKey(keyName)) resDic.Remove(keyName); } }; resDic.Add(keyName, handle); } public void Release <T >(string name ) { string keyName = name + "_" + typeof (T).Name; if (!resDic.ContainsKey(keyName)) return ; Addressables.Release((AsyncOperationHandle<T>)resDic[keyName]); resDic.Remove(keyName); } public void Clear () { resDic.Clear(); AssetBundle.UnloadAllAssetBundles(true ); Resources.UnloadUnusedAssets(); GC.Collect(); } }