|
Unity资源读取:
- RM: Resources.Load
- Addressables: Addressables.LoadAssetAsync
- AssetBundle:AssetBundle.Load
<hr/>问题:
- 读取
- RM:只能读取Resources文件夹下的(即路径中包含Resources),并且是按照相对路径读取
- Addressables:需要手动设置group和key,再根据key去读取并且无法根据类型读取
- AssetBundle:需要手动设置繁琐的依赖关系来达成最佳的内存利用率
- 优化问题
- RM:资源包无法分块读取,内存占用大,加载速度慢,需要一整块资源加载
- Addressables:group关系并不影响内存关系,哪里需要加载哪里
- AssetBundle:复杂的内存管理,一不小心就内存泄露了
- 热更新
- RM:不存在的 (ノ`Д)ノ
- Addressables:快捷方便,通过group设置,可以自动对比出补丁包
- AssetBundle:需要自己管理特征码和包体差异等对比
<hr/>我们需要:
- 简单快捷的读取方式:不管是Resources文件夹以内的还是以外,根据文件名读取和根据类型读取
- 复杂的依赖关系可以更易于管理:自动管理资源
- 高效清晰并易于管理的内存管理:Adressables的非依赖的资源读取方式
- 快捷方便的热更新方案:自动检测出补丁包以便增量更新
<hr/>ResourcesManager-基于Addressables和RM整合出一套资源管理和打包流程
<hr/>简单快捷的读取方式
1. 以文件名为key,建立资源映射表
- Refresh all directroy information 刷新映射表
- Directories 目标文件夹
- Exclude Directories 排除的文件
- Ignore Extensions 忽略的文件类型
- Auto Detect 是否在文件导入或移动时自动刷新
- 映射表
- 注意事项:同名文件会被强制加入_type,如GameObject.prefab有同名的话会变化GameObject_prefab.prefab
<hr/>2. 读取资源的接口
/// <summary>
/// Load resources according to name
/// </summary>
/// <typeparam name=&#34;T&#34;>Any types Inherited from UnityEngine.Object</typeparam>
/// <param name=&#34;assetName&#34;>Resource name</param>
/// <returns>Resource</returns>
public T LoadFromName<T>(string assetName) where T : UnityEngine.Object ···
/// <summary>
/// Async load resources according to name
/// </summary>
/// <typeparam name=&#34;T&#34;>Any types Inherited from UnityEngine.Object</typeparam>
/// <param name=&#34;assetName&#34;>Resource name</param>
/// <param name=&#34;callback&#34;>Callback will be invoke when load done</param>
/// <returns>Coroutine for async</returns>
public Coroutine LoadFromNameAsync<T>(string assetName, Action<T> callback) where T : UnityEngine.Object ···
internal T LoadFromNameInernal<T>(string assetName, Action<T> callback, out Coroutine coroutine) where T : UnityEngine.Object ···
internal IEnumerator Load<T>(string assetName, Action<T> callback) where T : UnityEngine.Object ···
- 根据名字读取资源
- LoadFromName:同步读取资源,所有被包括在映射表内的资源都可以直接用这个方法
- 内部用了Resources.Load和Addressables.LoadAssetAsync的接口
- 注意:Addressables的同步读取接口是官方非正式公开的,可能有点问题
- 目前Addressables也没有开放根据type来读取资源的接口,只有泛型
- LoadFromNameAsync:异步读取接口,其他同上
/// <summary>
/// Load all types of target
/// </summary>
/// <typeparam name=&#34;T&#34;>Any types Inherited from UnityEngine.Object</typeparam>
/// <param name=&#34;path&#34;>Optional: Start such as Resources/Folder</param>
/// <returns>List for all resources</returns>
public List<T> LoadFromType<T>(string path = null) where T : UnityEngine.Object ···
/// <summary>
/// Load all types of target
/// </summary>
/// <param name=&#34;type&#34;>Any types Inherited from UnityEngine.Object</param>
/// <param name=&#34;path&#34;>Optional: Start such as Resources/Folder</param>
/// <returns>List for all resources</returns>
public List<UnityEngine.Object> LoadFromType(Type type, string path = null) ···
/// <summary>
/// Async load all types of target
/// </summary>
/// <typeparam name=&#34;T&#34;>Any types Inherited from UnityEngine.Object</typeparam>
/// <param name=&#34;callback&#34;>Callback will be invoke when load done</param>
/// <param name=&#34;path&#34;>Optional: Start such as Resources/Folder</param>
public void LoadFromTypeAsyn<T>(Action<List<T>> callback, string path = null) where T : UnityEngine.Object ···
/// <summary>
/// Async load all types of target
/// </summary>
/// <param name=&#34;type&#34;>Any types Inherited from UnityEngine.Object</param>
/// <param name=&#34;callback&#34;>Callback will be invoke when load done</param>
/// <param name=&#34;path&#34;>Optional: Start such as Resources/Folder</param>
public void LoadFromTypeAsyn(Type type, Action<List<UnityEngine.Object>> callback, string path = null) ···
internal List<UnityEngine.Object> LoadFromTypeInternal(Type type, string path = null, Action<List<UnityEngine.Object>> callback = null) ···
- 根据类型读取资源
- LoadFromType:同步读取path路径下所有type的资源,如果path为空,则遍历映射表
- Resources.Load、Addressables.LoadAssetAsync
- 根据映射表的Unity类型读取
- 非Resources的同步接口同上,只能用AssetDataBase
- LoadFromTypeAsync:异步读取接口,其他同上
<hr/>自动管理资源
- 通过资源导入或移动的事件(AssetPostprocessor)回调修改映射表
- 自动添加或修改删除Addressables中的Group和Entry,当资源未被指定Addressables时,自动加入到Temp(Group)中
<hr/>示例
- 将Static Resources加入到ResoucesManager的Directories中
2. 确认添加之前的状态,映射表和Addressables
3. 加入GameObject1的预设到Static Resources中
4. 映射表自动加入了GameObject1的映射关系,Addressables也自动加入到Default(Group)并创建一个Entry用于GameObject1的定向
5. Build Player Content
6. 选择Pack Play Mode(模拟实机加载)
7. 代码加载
public class Test : MonoBehaviour
{
private void Start()
{
ResourcesManager.instance.LoadFromNameAsync<GameObject>(&#34;GameObject1&#34;, go => { Instantiate(go); });
}
}
8. 没问题
Adressables的非依赖的资源读取方式
参考之前写的资源管理
增量更新
- 官方文档
- 无需在导入或修改时特地分离出更新内容,自动快捷地自动识别出需要更新的资源,并分到到单独一个Group里面,打出单独的asset包用于增量更新
<hr/>
- 给Group指定好Static Content注明Group中的内容是静态不会更新的
2. 来到相关资源包文件夹检查
3. 修改GameObject2(pos.x+1),新增GameObject3
4. 选择Prepare For Content Update
5. 选择Asset/AddressableAssetsData内的bin文件(记录了上次资源包的相关内容)
6. 检测出相关新增和修改,将相关的Entry会移动到Content Update(Group)中
7. 修改Content Update的设置
8. 再次打包,相关资源包文件夹多出了用于增量更新的资源
Addressables的同步读取
参考自Unity官方示例
2. 看了看脚本核心部分,其实重写解包和读取资源的方式,用了AssetBundle
3. Default Group设置解包和读取资源的Provider
注意:一定要设置Default的Group,而且资源所在Group也需要,我这里就讲需要打包资源所在Group设为Default了
4. 打包
5.脚本读取
private void Start()
{
StartCoroutine(Load());
}
private IEnumerator Load()
{
//由于Addressables的初始化是异步过程,MainThread无法通过进程阻塞来等待初始化完成,只能这样了
yield return ResourcesManager.instance.inited;
//第一次异步读取需要等待初始化,之后就不需要了
Instantiate(ResourcesManager.instance.LoadFromName<GameObject>(&#34;Cube&#34;));
Instantiate(ResourcesManager.instance.LoadFromName<GameObject>(&#34;GameObject1&#34;));
}
//ResoucesManager内部相关读取
var handle = Addressables.LoadAssetAsync<T>(assetName);
if (handle.IsValid() && handle.Status == AsyncOperationStatus.Succeeded)
{
if (callback == null)
{
if (handle.IsDone)
{
return handle.Result;
}
}
···
}
6. 实机运行,没问题 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|