6.资源加载基础-动态加载单个资源
6.1 知识点
通过资源名或标签名动态加载单个资源
- 命名空间:UnityEngine.AddressableAssets 和 UnityEngine.ResourceManagement.AsyncOperations
Addressables.LoadAssetAsync 动态加载单个资源 传入的参数可以是资源名或标签名
// Addressables.LoadAssetAsync 动态加载单个资源 传入的参数可以是资源名或标签名
asyncOperationHandle = Addressables.LoadAssetAsync<GameObject>("Cube");
asyncOperationHandle.Completed += (handle) =>
{
//判断加载成功
if (handle.Status == AsyncOperationStatus.Succeeded)
Instantiate(handle.Result);
};
一步到位的Addressables.LoadAssetAsync
Addressables.LoadAssetAsync<GameObject>("Red").Completed += (asyncOperationHandle) =>
{
//判断加载成功
if (asyncOperationHandle.Status == AsyncOperationStatus.Succeeded)
Instantiate(asyncOperationHandle.Result);
};
注意:
- 重复加载相同资源不会报错,但是没有对应的Key会报错
- 如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
- 如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个
释放资源
Addressables.Release 要释放哪一个返回值
//直接写在这不合理 要加载完了再释放
//Addressables.Release(asyncOperationHandle);
asyncOperationHandle = Addressables.LoadAssetAsync<GameObject>("Cube");
asyncOperationHandle.Completed += (handle) =>
{
//判断加载成功
if (handle.Status == AsyncOperationStatus.Succeeded)
Instantiate(handle.Result);
//一定要是 加载完成后 使用完毕后 再去释放
//不管任何资源 只要释放后 都会影响之前在使用该资源的对象 要在AB包模式下才能看出
//所以要保证不再使用该资源后才释放
//和通过标识加载不同 标识加载GameObjetct实例化后释放不受影响
Addressables.Release(handle);
};
//在对象被删除后释放是较为合理的
private void OnDestroy()
{
Addressables.Release(asyncOperationHandle);
}
动态加载场景
Addressables.LoadSceneAsync 动态加载场景
SceneInstance.ActivateAsync 手动激活异步加载的场景
//参数一:场景名 场景名是场景资源在Group下的名字
//参数二:加载模式 (叠加还是单独,叠加就是两个场景一起显示,单独就是只保留新加载的场景,正常情况为单独)
//参数三:场景加载是否激活,如果为false,加载完成后不会直接切换,需要自己使用返回值中的ActivateAsync方法
//参数四:场景加载的异步操作优先级 一般不填
Addressables.LoadSceneAsync("SampleScene", UnityEngine.SceneManagement.LoadSceneMode.Single, false)
//给场景加载完成后添加回调
.Completed += (handle) =>
{
Debug.Log("异步场景加载完成");
//比如说 手动激活场景 异步激活后也可以添加完成回调
//加载场景的话handle.Result是SceneInstance
handle.Result.ActivateAsync().completed += (a) =>
{
Debug.Log("异步激活场景完成");
//然后再去创建场景上的对象
//然后再去隐藏 加载界面
//注意:场景资源也是可以释放的,并不会影响当前已经加载出来的场景,因为场景的本质只是配置文件
Addressables.Release(handle);
};
};
总结
- 根据名字或标签加载单个资源相对之前的指定加载资源更加灵活
- 主要通过Addressables类中的静态方法传入资源名或标签名进行加载
- 注意:
- 如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
- 如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个
- 释放资源时需要传入之前记录的AsyncOperationHandle对象
- 注意:一定要保证资源使用完毕过后再释放资源
- 场景异步加载可以自己手动激活加载完成的场景
6.2 知识点代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class Lesson06_资源加载基础_动态加载单个资源 : MonoBehaviour
{
AsyncOperationHandle<GameObject> asyncOperationHandle;
void Start()
{
#region 知识点一 通过资源名或标签名动态加载单个资源
//命名空间:
//UnityEngine.AddressableAssets 和 UnityEngine.ResourceManagement.AsyncOperations
// Addressables.LoadAssetAsync 动态加载单个资源 传入的参数可以是资源名或标签名
asyncOperationHandle = Addressables.LoadAssetAsync<GameObject>("Cube");
asyncOperationHandle.Completed += (handle) =>
{
//判断加载成功
if (handle.Status == AsyncOperationStatus.Succeeded)
Instantiate(handle.Result);
//一定要是 加载完成后 使用完毕后 再去释放
//不管任何资源 只要释放后 都会影响之前在使用该资源的对象 要在AB包模式下才能看出
//所以要保证不再使用该资源后才释放
//和通过标识加载不同 标识加载GameObjetct实例化后释放不受影响
Addressables.Release(handle);
};
//一步到位
//Addressables.LoadAssetAsync<GameObject>("Red").Completed += (asyncOperationHandle) =>
//{
// //判断加载成功
// if (asyncOperationHandle.Status == AsyncOperationStatus.Succeeded)
// Instantiate(asyncOperationHandle.Result);
//};
//注意:
//1.如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
//2.如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个
#endregion
#region 知识点二 释放资源
//Addressables.Release 要释放哪一个返回值
//写在这不合理 要加载完了在释放
//Addressables.Release(asyncOperationHandle);
#endregion
#region 知识点三 动态加载场景
// Addressables.LoadSceneAsync 动态加载场景
//参数一:场景名 场景名是场景资源在Group下的名字
//参数二:加载模式 (叠加还是单独,叠加就是两个场景一起显示,单独就是只保留新加载的场景,正常情况为单独)
//参数三:场景加载是否激活,如果为false,加载完成后不会直接切换,需要自己使用返回值中的ActivateAsync方法
//参数四:场景加载的异步操作优先级
Addressables.LoadSceneAsync("SampleScene", UnityEngine.SceneManagement.LoadSceneMode.Single, false)
//给场景加载完成后添加回调
.Completed += (handle) =>
{
Debug.Log("异步场景加载完成");
//比如说 手动激活场景 异步激活后也可以添加完成回调
handle.Result.ActivateAsync().completed += (a) =>
{
Debug.Log("异步激活场景完成");
//然后再去创建场景上的对象
//然后再去隐藏 加载界面
//注意:场景资源也是可以释放的,并不会影响当前已经加载出来的场景,因为场景的本质只是配置文件
Addressables.Release(handle);
};
};
#endregion
#region 知识点四 总结
//1.根据名字或标签加载单个资源相对之前的指定加载资源更加灵活
// 主要通过Addressables类中的静态方法传入资源名或标签名进行加载
// 注意:
// 1-1.如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
// 1-2.如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个
//2.释放资源时需要传入之前记录的AsyncOperationHandle对象
// 注意:一定要保证资源使用完毕过后再释放资源
//3.场景异步加载可以自己手动激活加载完成的场景
#endregion
}
//在对象被删除后释放是较为合理的
private void OnDestroy()
{
//Addressables.Release(asyncOperationHandle);
}
}
6.3 练习题
尝试自己写一个Addressables资源管理器,帮助我们通过名字加载单个资源或场景,并管理资源相关内容
分析
- 资源动态加载可以通过返回的对象AsyncOperationHandle<>(异步操作句柄) 添加完成监听来处理加载后的资源
- 释放资源时也是通过释放返回的对象AsyncOperationHandle<>(异步操作句柄) 来进行释放
- 如果分散在各脚本中自己管理资源难免显得太过凌乱,所以我们可以通过一个资源管理器来管理所有的异步加载返回对象AsyncOperationHandle<>(异步操作句柄)
//正常单资源加载的写法
AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Cube");
handle.Completed += (obj) => {
//加载成功后的逻辑处理
Addressables.Release(obj);
};
创建AddressablesManager,实现单例
public class AddressablesManager : BaseSingletonInMonoBehaviour<AddressablesManager>
{
}
定义存储AsyncOperationHandle的容器
//有一个容器 帮助我们存储各个异步加载的返回值AsyncOperationHandle AsyncOperationHandle继承IEnumerator 所以把IEnumerator作为值方便里氏替换原则
public Dictionary<string, IEnumerator> resDic = new Dictionary<string, IEnumerator>();
实现异步加载资源的方法
//异步加载资源的方法 传入资源名 和回调
public void LoadAssetAsync<T>(string name, Action<AsyncOperationHandle<T>> callBack)
{
//由于存在同名 不同类型资源的区分加载
//所以我们通过名字和类型拼接作为 key
string keyName = name + "_" + typeof(T).Name;
AsyncOperationHandle<T> handle;
//如果已经加载过该资源
if (resDic.ContainsKey(keyName))
{
//获取异步加载返回的操作内容
handle = (AsyncOperationHandle<T>)resDic[keyName];
//判断 这个异步加载是否结束
if (handle.IsDone)
{
//如果成功 就不需要异步了 直接相当于同步调用了 这个委托函数 传入对应的返回值
callBack(handle);
}
//还没有加载完成
else
{
//如果这个时候 还没有异步加载完成 那么我们只需要 告诉它 完成时做什么就行了
handle.Completed += (obj) =>
{
if (obj.Status == AsyncOperationStatus.Succeeded)
callBack(obj);
};
}
return;
}
//如果没有加载过该资源
//直接进行异步加载 并且记录
handle = Addressables.LoadAssetAsync<T>(name);
handle.Completed += (obj) =>
{
if (obj.Status == AsyncOperationStatus.Succeeded)
callBack(obj);
else
{
Debug.LogWarning(keyName + "资源加载失败");
if (resDic.ContainsKey(keyName))
resDic.Remove(keyName);
}
};
resDic.Add(keyName, handle);
}
实现释放资源的方法
//释放资源的方法
public void Release<T>(string name)
{
//由于存在同名 不同类型资源的区分加载
//所以我们通过名字和类型拼接作为 key
string keyName = name + "_" + typeof(T).Name;
if (resDic.ContainsKey(keyName))
{
//取出对象 移除资源 并且从字典里面移除
AsyncOperationHandle<T> handle = (AsyncOperationHandle<T>)resDic[keyName];
Addressables.Release(handle);
resDic.Remove(keyName);
}
}
实现清空所有资源的方法
//清空资源
public void Clear()
{
resDic.Clear();
AssetBundle.UnloadAllAssetBundles(true);
Resources.UnloadUnusedAssets();
GC.Collect();
}
进行测试
AddressablesManager.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
{
Instantiate(obj.Result);
});
AddressablesManager.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
{
Instantiate(obj.Result, Vector3.right * 5, Quaternion.identity);
//使用完资源后 移除资源 注意非AB包模式下Cube还会存在
AddressablesManager.Instance.Release<GameObject>("Cube");
});
// AddressablesManager.Instance.Clear();
6.4 练习题代码
AddressablesManager
using System;
using System.Collections;
using System.Collections.Generic;
using BaseFramework;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
namespace BaseFramework
{
public class AddressablesManager : BaseSingletonInMonoBehaviour<AddressablesManager>
{
//有一个容器 帮助我们存储各个异步加载的返回值AsyncOperationHandle AsyncOperationHandle继承IEnumerator 所以把IEnumerator作为值方便里氏替换原则
public Dictionary<string, IEnumerator> resDic = new Dictionary<string, IEnumerator>();
//异步加载资源的方法 传入资源名 和回调
public void LoadAssetAsync<T>(string name, Action<AsyncOperationHandle<T>> callBack)
{
//由于存在同名 不同类型资源的区分加载
//所以我们通过名字和类型拼接作为 key
string keyName = name + "_" + typeof(T).Name;
AsyncOperationHandle<T> handle;
//如果已经加载过该资源
if (resDic.ContainsKey(keyName))
{
//获取异步加载返回的操作内容
handle = (AsyncOperationHandle<T>)resDic[keyName];
//判断 这个异步加载是否结束
if (handle.IsDone)
{
//如果成功 就不需要异步了 直接相当于同步调用了 这个委托函数 传入对应的返回值
callBack(handle);
}
//还没有加载完成
else
{
//如果这个时候 还没有异步加载完成 那么我们只需要 告诉它 完成时做什么就行了
handle.Completed += (obj) =>
{
if (obj.Status == AsyncOperationStatus.Succeeded)
callBack(obj);
};
}
return;
}
//如果没有加载过该资源
//直接进行异步加载 并且记录
handle = Addressables.LoadAssetAsync<T>(name);
handle.Completed += (obj) =>
{
if (obj.Status == AsyncOperationStatus.Succeeded)
callBack(obj);
else
{
Debug.LogWarning(keyName + "资源加载失败");
if (resDic.ContainsKey(keyName))
resDic.Remove(keyName);
}
};
resDic.Add(keyName, handle);
}
//释放资源的方法
public void Release<T>(string name)
{
//由于存在同名 不同类型资源的区分加载
//所以我们通过名字和类型拼接作为 key
string keyName = name + "_" + typeof(T).Name;
if (resDic.ContainsKey(keyName))
{
//取出对象 移除资源 并且从字典里面移除
AsyncOperationHandle<T> handle = (AsyncOperationHandle<T>)resDic[keyName];
Addressables.Release(handle);
resDic.Remove(keyName);
}
}
//清空资源
public void Clear()
{
resDic.Clear();
AssetBundle.UnloadAllAssetBundles(true);
Resources.UnloadUnusedAssets();
GC.Collect();
}
}
}
Lesson06_练习题
using System.Collections;
using System.Collections.Generic;
using BaseFramework;
using UnityEngine;
public class Lesson06_练习题 : MonoBehaviour
{
void Start()
{
//尝试自己写一个Addressables资源管理器,帮助我们通过名字加载单个资源或场景,并管理资源相关内容
//AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Cube");
//handle.Completed += (obj) => {
// //加载成功后的逻辑处理
// Addressables.Release(obj);
//};
//分析:
//1.资源动态加载可以通过返回的对象AsyncOperationHandle<>(异步操作句柄) 添加完成监听来处理加载后的资源
//2.释放资源时也是通过释放返回的对象AsyncOperationHandle<>(异步操作句柄) 来进行释放
//3.如果分散在各脚本中自己管理资源难免显得太过凌乱,所以我们可以通过一个资源管理器来管理所有的异步加载返回对象AsyncOperationHandle<>(异步操作句柄)
//所以如果我们要自己写一个Addressables资源管理器,主要就是用来管理AsyncOperationHandle<>对象的
AddressablesManager.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
{
Instantiate(obj.Result);
});
AddressablesManager.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
{
Instantiate(obj.Result, Vector3.right * 5, Quaternion.identity);
//使用完资源后 移除资源 注意非AB包模式下Cube还会存在
AddressablesManager.Instance.Release<GameObject>("Cube");
});
// AddressablesManager.Instance.Clear();
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com