unityAB包(2/2)
unityAB包(2/2)
- unity
- 1.AB包的依赖问题
- 2.AB包资源管理器
- 先导:饿汉模式与懒汉模式
- 饿汉模式
- 懒汉模式
- AB资源管理器主代码
unity
1.AB包的依赖问题
例如一个Prefab被添加到了AB包中,该包会默认添加这个预设体带有的材质等,然而如果你手动把相关的材质添加到了另外的AB包中,此时该材质就不会添加到预设体的AB包中(可能是出于性能考虑机制),则一定要引用预设体对应的包和材质所对应的包,否则会出现材质错误。
然而显而易见的是当这个预设体的引用来自各个不同的AB包中,那么一条一条引入包的代码就会显得很冗余。所以引入了下面这个方法。
通过给定的API得到所有依赖的信息,然后用一个string数组存储这些依赖包的名字,通过遍历依赖名字数组导入所有的依赖。
AssetBundle abMain = AssetBundle.LoadFromFile (Application.streamingAssetsPath + "/" + "PC");
// 加载主包中的固定文件
AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
// 从固定文件中 得到依赖信息
string [] strs = abManifest.GetAllDependencies ("model");
// 得到了 依赖包的名字
for (int i = 0; i < strs.Length; i++)
{
AssetBundle.LoadFromFile (Application.streamingAssetsPath
+ "/" + strs [i]);
}
这种方法不能对一个AB包中的预设体进行区分,例如A刚需EF包,B刚需OL包,
然而我项目中只使用了A,然而在检查依赖的时候会一同加载不被需要的OL包。
2.AB包资源管理器
先导:饿汉模式与懒汉模式
饿汉模式
在类建立之初就创建一个只读的对象,确保唯一。
using UnityEngine;
public class EagerSingleton : MonoBehaviour
{
// 静态字段,用于存储单例实例
public static EagerSingleton instance;
private void Awake()
{
// 在Awake方法中进行实例的初始化
if (instance == null)
{
instance = this;
// 确保单例对象在场景切换时不会被销毁
DontDestroyOnLoad(gameObject);
}
else
{
// 如果已经存在实例,则销毁当前对象
Destroy(gameObject);
}
}
}
懒汉模式
引用自:博主YY-nb直达
确保只有在类的内部才能实例化,外部只能调用Instance方法。
使用lock确保在同一时间内只有一个进程在执行创建单例。
如果后续还有访问这个单例的,直接返回这个单例,不要再继续加锁了,影响性能。
public class Singleton
{
private static Singleton instance;
private static readonly object locker = new object();
private Singleton(){}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (locker)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
}
AB资源管理器主代码
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class AssetBundleManager : MonoBehaviour
{
public static AssetBundleManager Instance;
private Dictionary<string, AssetBundle> abDic;
private AssetBundle mainAB = null;
private AssetBundleManifest mainfest = null;
public string abUrl { get; private set; }
public string mainAbName { get; private set; }
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else if(Instance != this)
{
Destroy(gameObject);
}
}
private void Start()
{
abDic = new Dictionary<string, AssetBundle>();
abUrl = Application.streamingAssetsPath + "/";
mainAbName = "PC";
}
//加载依赖
public void LoadDepends(string abName)
{
//获取主包
if(mainAB == null)
{
mainAB = AssetBundle.LoadFromFile(abUrl + mainAbName); //固定语法 获取AB包配置文件
mainfest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
string[] temp = mainfest.GetAllDependencies(abName);//主包API获取小包的依赖
for(int i = 0; i < temp.Length; i++)
{
if (!abDic.ContainsKey(temp[i]))
{
AssetBundle tempab = AssetBundle.LoadFromFile(abUrl + temp[i]);//某个小包 依赖了这么多小包
abDic.Add(temp[i], tempab);
}
else
{
Debug.Log("again");
}
}
if (!abDic.ContainsKey(abName))
{
AssetBundle ab = AssetBundle.LoadFromFile(abUrl + abName);
abDic.Add(abName, ab);
}
else
{
Debug.Log("again");
}
}
// 同步加载
public T LoadRes<T>(string abName, string resName) where T : Object
{
Debug.Log("YSe");
LoadDepends(abName);
T res = abDic[abName].LoadAsset<T>(resName);
return res;
}
// 异步加载
// 异步加载依赖
public IEnumerator LoadDependsAsync(string abName)
{
// 获取主包
if (mainAB == null)
{
AssetBundleCreateRequest mainABRequest = AssetBundle.LoadFromFileAsync(abUrl + mainAbName);
yield return mainABRequest;
mainAB = mainABRequest.assetBundle;
mainfest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
//只要涉及获取等待的任务 都需要一个yield return
string[] temp = mainfest.GetAllDependencies(abName);
for (int i = 0; i < temp.Length; i++)
{
if (!abDic.ContainsKey(temp[i]))
{
AssetBundleCreateRequest tempAbRequest = AssetBundle.LoadFromFileAsync(abUrl + temp[i]);
yield return tempAbRequest;
AssetBundle tempab = tempAbRequest.assetBundle;
abDic.Add(temp[i], tempab);
}
else
{
Debug.Log("again");
}
}
if (!abDic.ContainsKey(abName))
{
AssetBundleCreateRequest abRequest = AssetBundle.LoadFromFileAsync(abUrl + abName);
yield return abRequest;
AssetBundle ab = abRequest.assetBundle;
abDic.Add(abName, ab);
}
else
{
Debug.Log("again");
}
}
// 异步加载资源
public IEnumerator LoadResAsync<T>(string abName, string resName, System.Action<T> onComplete) where T : Object
{
yield return StartCoroutine(LoadDependsAsync(abName));
AssetBundleRequest request = abDic[abName].LoadAssetAsync<T>(resName);
yield return request;
T res = abDic[abName].LoadAsset<T>(resName);
onComplete?.Invoke(res);
}
// 卸载单个包
public void UnLoad(string abName)
{
abDic[abName].Unload(false);
abDic.Remove(abName);
}
// 卸载所有包
public void UnLoadAll()
{
AssetBundle.UnloadAllAssetBundles(false);
abDic.Clear();
mainAB = null;
mainfest = null;
}
}
TEST脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//同步
GameObject gameObject = AssetBundleManager.Instance.LoadRes<GameObject>("model", "Cube");
Instantiate(gameObject,Vector3.up,Quaternion.identity);
GameObject gameObject2 = AssetBundleManager.Instance.LoadRes<GameObject>("model", "Cube");
Instantiate(gameObject2, Vector3.down, Quaternion.identity);
//异步
StartCoroutine(AssetBundleManager.Instance.LoadResAsync<GameObject>("model", "Cube", OnAssetLoaded));
}
//根据传出类型的不同 编写不同的OnXXX函数,然后订阅此函数
private void OnAssetLoaded(GameObject loadedAsset)
{
if (loadedAsset != null)
{
// 资源加载成功,实例化该资源
Instantiate(loadedAsset);
Debug.Log("资源加载并实例化成功!");
}
else
{
Debug.LogError("资源加载失败!");
}
}
// Update is called once per frame
void Update()
{
}
}