Unity AB管理
2021-06-18 本文已影响0人
___________6a1d
Assetbundle管理
核心:读配置表,依赖加载管理
读取AssetBundle配置表
设置中间类进行引用计数
根据路径加载AssetBundle
根据路径卸载AssetBundle
为ResourceManager提供加载中间类,根据中间类释放资源,查找中间类等方法
项目中新建一个空物体,名字为#StartCRT#,挂上以下脚本,用来加载配置文件:
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
/// <summary>
/// 加载AssetBundleConfig.bytes文件,这个必须在最开始运行
/// </summary>
public class StartCRT : MonoBehaviour
{
[HideInInspector]
public byte[] bytes = null;
[HideInInspector]
public AssetBundleConfig config = null;
// Use this for initialization
void Start()
{
StartCoroutine(Load());
}
IEnumerator Load()
{
string path = Application.streamingAssetsPath + "/AssetBundleConfig.bytes";
WWW www = new WWW(path);
yield return www;
if (www.isDone)
{
bytes = www.bytes;
MemoryStream memoryStream = new MemoryStream(bytes);
BinaryFormatter binaryFormatter = new BinaryFormatter();
config = (AssetBundleConfig)binaryFormatter.Deserialize(memoryStream);
memoryStream.Close();
}
}
}
AB管理类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
/// <summary>
/// AB管理类
/// </summary>
public class AssetBundleManager : Singleton<AssetBundleManager>
{
private StartCRT m_startCRT;//为了开启协成加载bytes文件
/// <summary>
/// 资源关系配置表
/// 可以根据CRC找到对应资源块
/// </summary>
protected Dictionary<uint, ResourceItem> m_ResourceItemDict = new Dictionary<uint, ResourceItem>();
/// <summary>
/// 储存已加载的AB包
/// key为CRC
/// </summary>
protected Dictionary<uint, AssetBundleItem> m_AssetBundleItemDict = new Dictionary<uint, AssetBundleItem>();
/// <summary>
/// AssetBundleItem类对象池(因为有些对象经常可能用到)
/// </summary>
protected ClassObjectPool<AssetBundleItem> m_AssetBundleItemPool = ObjectManager.Instance.GetOrCreateClassPool<AssetBundleItem>(500);
/// <summary>
/// 加载AB配置表,返回是否正确加载,在游戏开始场景调用
/// </summary>
/// <returns></returns>
public bool LoadAssetBundleConfig()
{
m_ResourceItemDict.Clear();
m_startCRT = GameObject.Find("#StartCRT#").GetComponent<StartCRT>();
if (m_startCRT.bytes == null)
{
Debug.LogError("AssetBundleConfig is not exist!");
return false;
}
for (int i = 0; i < m_startCRT.config.ABList.Count; i++)
{
ABBase abBase = m_startCRT.config.ABList[i];
//使用中间类进行保存
ResourceItem item = new ResourceItem();
item.m_Crc = abBase.Crc;
item.m_AssetName = abBase.AssetName;
item.m_ABName = abBase.ABName;
item.m_DependedAssetBundle = abBase.ABDpendece;
if (m_ResourceItemDict.ContainsKey(item.m_Crc))
{
Debug.LogError("重复的CRC,资源名:" + item.m_AssetName + "AB包名:" + item.m_ABName);
}
else
{
m_ResourceItemDict.Add(item.m_Crc, item);
}
}
return true;
}
/// <summary>
/// 根据路径的CRC加载中间类RecourseItem
/// </summary>
/// <param name="crc"></param>
/// <returns></returns>
public ResourceItem LoadResourceAssetBundle(uint crc)
{
ResourceItem item = null;
//未找到资源的情况下
if (!m_ResourceItemDict.TryGetValue(crc, out item) || item == null)
{
Debug.Log(string.Format("LoadResourceAssetBundle Error:can not find crc {0} in AssetBundleConfig", crc.ToString()));
return item;
}
//证明这个ab被加载了,直接返回
if (item.m_AssetBundle != null)
{
return item;
}
item.m_AssetBundle = LoadAssetBundle(item.m_ABName);
//加载另外的AB
if (item.m_DependedAssetBundle != null)
{
for (int i = 0; i < item.m_DependedAssetBundle.Count; i++)
{
LoadAssetBundle(item.m_DependedAssetBundle[i]);
}
}
return item;
}
/// <summary>
/// 根据名字加载单个AssetBundle
/// 有可能会出现这种情况,A和B都依赖了C资源,加载A和B的话就会加载两次(第二次会报错),如果卸载了A依赖的C,那B可能就显示不正常了
/// 因此采用引用计数,加载时+1,判断卸载的时候引用计数为0的时候才让卸载
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
private AssetBundle LoadAssetBundle(string name)
{
AssetBundleItem item = null;
uint crc = Crc32.GetCrc32(name);
if (!m_AssetBundleItemDict.TryGetValue(crc, out item))
{
AssetBundle ab = null;
string fullPath = Application.streamingAssetsPath + "/" + name;
if (File.Exists(fullPath))
{
ab = AssetBundle.LoadFromFile(fullPath);
}
if (ab == null)
{
Debug.LogError("LoadAssetBundle error:" + fullPath);
}
item = m_AssetBundleItemPool.Spawn(true);
item.assetBundle = ab;
item.refCount++;
m_AssetBundleItemDict.Add(crc, item);
}
else
{
item.refCount++;
}
return item.assetBundle;
}
/// <summary>
/// 释放资源
/// </summary>
/// <param name="item"></param>
public void ReleaseAsset(ResourceItem item)
{
if (item == null)
{
return;
}
if (item.m_DependedAssetBundle != null && item.m_DependedAssetBundle.Count >= 0)
{
for (int i = 0; i < item.m_DependedAssetBundle.Count; i++)
{
UnLoadAssetBundle(item.m_DependedAssetBundle[i]);
}
}
UnLoadAssetBundle(item.m_ABName);
}
/// <summary>
/// 卸载依赖包
/// </summary>
/// <param name="name"></param>
private void UnLoadAssetBundle(string name)
{
AssetBundleItem item = null;
uint crc = Crc32.GetCrc32(name);
if (m_AssetBundleItemDict.TryGetValue(crc, out item) && item != null)
{
item.refCount--;
if (item.refCount <= 0 && item.assetBundle != null)
{
item.assetBundle.Unload(true);
item.Reset();
m_AssetBundleItemPool.Recycle(item);
m_ResourceItemDict.Remove(crc);
}
}
}
/// <summary>
/// 根据crc查找ResourcesItem
/// </summary>
/// <param name="crc"></param>
/// <returns></returns>
public ResourceItem FindResourceItem(uint crc)
{
return m_ResourceItemDict[crc];
}
}
/// <summary>
/// AB Item
/// </summary>
public class AssetBundleItem
{
public AssetBundle assetBundle = null;
//引用计数
public int refCount;
//卸载时的话需要还原
public void Reset()
{
assetBundle = null;
refCount = 0;
}
}
/// <summary>
/// 资源item
/// </summary>
public class ResourceItem
{
/// <summary>
/// 资源路径CRC
/// </summary>
public uint m_Crc = 0;
/// <summary>
/// 该资源文件名字
/// </summary>
public string m_AssetName = string.Empty;
/// <summary>
/// 该资源所在的AssetBundle名字
/// </summary>
public string m_ABName = string.Empty;
/// <summary>
/// 该资源所依赖的AssetBundle
/// </summary>
public List<string> m_DependedAssetBundle = null;
/// <summary>
/// 该资源所在的AssetBundle名字
/// </summary>
public AssetBundle m_AssetBundle = null;
}