更新日期:2025年4月15日。
Github 仓库:https://github.com/SaiTingHu/HTFramework
Gitee 仓库:https://gitee.com/SaiTingHu/HTFramework
索引
一、Addressables 可寻址系统
Addressables
可寻址系统是迄今为止最完善的Unity资源管理方案,对资源的加载、打包甚至是部署都实现了流水线式的封装。
所以,是时候将Addressables
加入到框架的资源加载策略中了。
不过,针对一些小型的项目、小游戏等,我们依然建议使用Resource
加载模式或AssetBundle
加载模式,因为Addressables
会带来一定的资源管理复杂度,会增加维护成本。
二、使用 Addressables 可寻址系统
在框架中使用Addressables
将极其简单,甚至原来使用Resource
加载模式或AssetBundle
加载模式的代码都不需要做出任何修改,就能直接无缝切换到Addressables
模式。
1.导入 Addressables
首先是从包管理器中导入Addressables
包,目前框架支持的Addressables
版本为1.20.0
至1.30.0
之间的任何版本(后续会根据Addressables
的版本迭代实时跟进):
2.切换到 Addressables 加载模式
将Resource
资源管理器模块的加载模式切换为Addressables
模式:
3.切换资源加载助手
加载模式切换为Addressables
模式后,我们会看到一条红色的错误提示:
DefaultResourceHelper(缺省的资源加载助手)不支持使用 Addressables 模式!
所以我们必须要更换资源加载助手
,更换为HT.Framework.AddressablesHelper
:
它即是缺省的资源加载助手(Addressables 模式)
。
4.加载资源
此时,我们即可按常规方式加载资源(Main.m_Resource.Load...
),在外部调用上,Resource
、AssetBundle
、Addressables
三种模式可以没有任何区别。
我们举个例子,将HTFrameworkDemo
中的Resource
场景切换为Addressables
模式。
其原本是使用的AssetBundle
模式,旨在演示如何使用AssetBundle
加载预制体、材质球、场景等资源。
首先,将加载模式
和资源管理器助手
按上文描述的进行设置:
然后,将需要加载的资源标记为可寻址
:
再者,运行场景,所有资源就能正确加载了:
是不是极其简单?且能做到代码零修改的无缝切换!
而且,原来的AssetBundle
模式使用的是单线加载
策略,每一个资源加载时都可能会存在等待时间,也即是等待当前加载的线路完成:
而Addressables
模式不使用单线加载
策略,所以在上上图中我们可以看到加载日志
中是没有等待耗时
这个参数的。
5.卸载资源
由于Addressables
采用引用计数模式,所以加载的资源必须在使用完成后卸载,且加载
次数与卸载
次数必须一致,否则由于该资源的引用计数还大于0,将无法彻底被卸载。
但在咱们这里无需关心引用计数
,无论一个资源加载了多少次,当你不再需要他时,只需调用一次卸载即可。
按如下方式进行卸载资源:
//加载资源
GameObject obj1 = null;
GameObject obj2 = null;
yield return Main.m_Resource.LoadPrefab("Assets/Cube.prefab", null, null, (o) =>
{
obj1 = o;
});
yield return Main.m_Resource.LoadPrefab("Assets/Cube.prefab", null, null, (o) =>
{
obj2 = o;
});
//使用资源......
//卸载资源前,上面的obj1、obj2(资源创建的实例)需手动销毁(Destroy),否则这2个实例将表现出材质丢失、动画丢失等
Destroy(obj1);
Destroy(obj2);
//卸载资源(参数为资源定位key),只需卸载一次
Main.m_Resource.UnLoadAsset("Assets/Cube.prefab");
6.注意事项
我们回看Demo
场景中加载资源的代码:
/// <summary>
/// 资源测试
/// </summary>
public class ResourceTest : MonoBehaviour
{
private SceneInfo _scene = new SceneInfo("scene", "Assets/HTFrameworkDemo/Script/Resource/TestScene.unity", "TestScene");
private PrefabInfo _cube = new PrefabInfo("cube", "Assets/HTFrameworkDemo/Script/Resource/Cube.prefab", null);
private PrefabInfo _capsule = new PrefabInfo("capsule", "Assets/HTFrameworkDemo/Script/Resource/Capsule.prefab", null);
private PrefabInfo _sphere = new PrefabInfo("sphere", "Assets/HTFrameworkDemo/Script/Resource/Sphere.prefab", null);
private AssetInfo _redMat = new AssetInfo("redmat", "Assets/HTFrameworkDemo/Script/Resource/Red.mat", null);
private List<GameObject> _prefabs = new List<GameObject>();
private Material _red;
private void Awake()
{
Main.m_Resource.SetAssetBundlePath(Application.dataPath + "/HTFrameworkDemo/Script/Resource/AB/");
}
private void Start()
{
//加载三个预制体,他们关联的AB包会被自动加载
Main.m_Resource.LoadPrefab(_cube, null, OnLoading, OnLoadDone);
Main.m_Resource.LoadPrefab(_capsule, null, OnLoading, OnLoadDone);
Main.m_Resource.LoadPrefab(_sphere, null, OnLoading, OnLoadDone);
}
private void OnGUI()
{
if (GUILayout.Button("全部替换为红色材质"))
{
StartCoroutine(LoadRedMat());
}
if (GUILayout.Button("加载TestScene场景"))
{
Main.m_Resource.LoadScene(_scene);
}
if (GUILayout.Button("卸载TestScene场景"))
{
Main.m_Resource.UnLoadScene(_scene);
}
if (GUILayout.Button("卸载所有场景"))
{
Main.m_Resource.UnLoadAllScene();
}
}
/// <summary>
/// 加载红色材质
/// </summary>
private IEnumerator LoadRedMat()
{
yield return null;
//等待加载完成
yield return Main.m_Resource.LoadAsset<Material>(_redMat, null, (mat) =>
{
Log.Info("加载红色材质完成!");
_red = mat;
});
for (int i = 0; i < _prefabs.Count; i++)
{
_prefabs[i].GetComponent<MeshRenderer>().material = _red;
}
}
private void OnLoadDone(GameObject arg)
{
Log.Info("加载完成:" + arg.name);
arg.SetActive(true);
arg.transform.position = Vector3.zero + new Vector3(0, 0, _prefabs.Count * 2);
Main.m_Controller.SetLookPoint(arg.transform.position);
_prefabs.Add(arg);
}
private void OnLoading(float arg)
{
Log.Info("加载中,进度:" + arg);
}
}
为了实现三种加载模式间的无缝切换,Addressables
模式加载资源时建议依然传入完整的资源信息
对象,比如:
PrefabInfo cube = new PrefabInfo("cube", "Assets/HTFrameworkDemo/Script/Resource/Cube.prefab", "Cube");
Main.m_Resource.LoadPrefab(_cube, null);
在这里,事实上Addressables
只关心第二个参数assetPath
,它是通过第二个参数去寻址资源的,改成下面这样也能正确加载:
PrefabInfo cube = new PrefabInfo(null, "Assets/HTFrameworkDemo/Script/Resource/Cube.prefab", null);
Main.m_Resource.LoadPrefab(_cube, null);
只不过,为了考虑无缝切换,第一个参数和第三个参数也建议正常传入。
当然,也支持直接传入寻址Key
进行加载:
Main.m_Resource.LoadPrefab("Assets/HTFrameworkDemo/Script/Resource/Cube.prefab", null);
还有就是,资源的寻址Key
建议始终设置为其相对路径
:
这样可以应对大多数变化,且此相对路径
永远都是唯一的。
关于Addressables
如何进行资源管理和配置,我这里就不做涉及了,看完下面这篇文章会使你醍醐灌顶: