自己写的AssetBundle框架设计,用十字链表来缓存已加载的ab,网上还有很多管理方式,比如引用计数,很多算法搞得很复杂无非也就是为了让Assetbundle根据引用计数的情况决定是否释放。如果A引用了B包,在A包还在内存的情况下,就不能卸载B包。构建出AB包之后可以直接把源素材删掉。资源卸载看这个
看上面那个链接,也就是说在从ab包加载出来的prefab被场景引用(实例化)或者代码引用(缓存)的时候,所使用到的贴图材质等ab包就不能被卸载,否则ab包就可以unload true了。
我看那些使用资源引用计数的人也是用了字典来缓存所有的ab包,每个ab包还都对应一个int值来计算引用计数的数量,我每次都加载资源的时候都会保留资源引用,根据卸载资源的引用来判断引用是否为0,为0的时候也会卸载当前ab包,至于被这个ab包所依赖,没有其他依赖的时候的其他ab包也不急着卸载,因为场景还有实例化的物体的时候材质包如果没了,也会出问题。
我也不知道对不对,也没个大神发个资源技术的框架源码教程来瞧瞧…
using System.IO;
using UnityEngine;
namespace ABFW
{
public class ABDefine
{
///
/// ab包的存放位置
///
///
///
#if UNITY_EDITOR
public static string GetABPackPath(string abName = null)
{
return Path.Combine(Application.streamingAssetsPath, "PC", abName ?? "PC");
}
#elif UNITY_STANDALONE
public static string GetABPackPath(string abName=null)
{
return Path.Combine(Application.persistentDataPath,"PC",abName??"PC");
}
#elif UNITY_ANDROID
public static string GetABPackPath(string abName=null)
{
return Path.Combine(Application.persistentDataPath,"Android",abName??"Android");
}
#elif UNITY_IPHONE
public static string GetABPackPath(string abName=null)
{
return Path.Combine(Application.persistentDataPath,"IOS",abName??"IOS");
}
#endif
///
/// 获得AB配置文件包名称
///
///
#if UNITY_EDITOR
public static string GetIniPath()
{
return Path.Combine(Application.streamingAssetsPath, "PC", "配置文件/ABIni.json");
}
#elif UNITY_STANDALONE
public static string GetIniPath()
{
return Path.Combine(Application.persistentDataPath, "PC", "配置文件/ABIni.json");
}
#elif UNITY_ANDROID
public static string GetIniPath()
{
return Path.Combine(Application.persistentDataPath, "PC", "配置文件/ABIni.json");
}
#elif UNITY_IPHONE
public static string GetIniPath()
{
return Path.Combine(Application.persistentDataPath, "PC", "配置文件/ABIni.json");
}
#endif
}
}
使用json管理
using System.Collections.Generic;
namespace ABFW
{
///
/// AB框架的配置文件
///
public class ABIni
{
public class Data
{
public string ResName;
public string ABName;
public Data(string resName, string abName)
{
this.ResName = resName;
this.ABName = abName;
}
}
public List<Data> Datas;
public ABIni()
{
Datas = new List<Data>();
}
public void AddData(string resName, string abName)
{
Datas.Add(new Data(resName, abName));
}
public void Clear()
{
Datas.Clear();
}
}
}
using UnityEngine;
namespace ABFW
{
public abstract class IABLoader
{
protected AssetBundle _AssetBundle;
protected string _ABName;
//是否是常驻Loader
protected bool IsPersistent;
public IABLoader(AssetBundle ab, string abName, bool isPersistent)
{
this._AssetBundle = ab;
this._ABName = abName;
this.IsPersistent = isPersistent;
}
///
/// 释放AssetBundle和资源缓存和被引用的资源内存
///
public abstract void Release();
}
}
using System.Collections;
using UnityEngine;
namespace ABFW
{
///
/// 类的职责:负责AB资源的加载,具有缓存
///
//只需要一个Loader
public class ABLoader : IABLoader
{
private Hashtable _ResCache;
//只有一种Manifest文件
public ABLoader(AssetBundle ab, string abName, bool isPersisent) : base(ab, abName, isPersisent)
{
//初始化缓存
_ResCache = new Hashtable();
}
public T LoadAsset<T>(string assetName) where T : UnityEngine.Object
{
if (_ResCache.ContainsKey(assetName))
{
Debug.Log($"找到缓存的Asset:{assetName}");
return _ResCache[assetName] as T;
}
T t = _AssetBundle.LoadAsset<T>(assetName);
_ResCache[assetName] = t;
return t;
}
///
/// 从内存以及缓存中卸载指定的资源
///
/// 资源名称
public void UnloadResource(string resName)
{
if (!_ResCache.ContainsKey(resName))
{
Debug.LogError("未缓存这样的资源");
}
else
{
//缓存中卸载
_ResCache.Remove(resName);
}
//资源引用计数已经为0
if (_ResCache.Count == 0)
Asset.Instance.factory.ReleaseABLoader(_AssetBundle.name);
}
public override void Release()
{
if (IsPersistent)
{
Debug.LogWarning($"这个是常驻AB,不可以卸载:{_AssetBundle.name}");
return;
}
_ResCache.Clear();
_AssetBundle.Unload(true);
}
}
}
using System.Collections.Generic;
using UnityEngine;
namespace ABFW
{
public class ABManifestLoader
{
protected AssetBundle _AssetBundle;
private Dictionary<string, string[]> _Dependences;
private AssetBundleManifest _ABManifest;
public ABManifestLoader(AssetBundle ab)
{
_AssetBundle = ab;
_ABManifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
_Dependences = new Dictionary<string, string[]>();
}
public string[] GetDependences(string abName)
{
string[] res;
if (_Dependences.TryGetValue(abName, out res))
{
return res;
}
res = _Dependences[abName] = _ABManifest.GetAllDependencies(abName);
Debug.Log($"在这里获得所有的依赖关系:{abName}的数量大概为:{res.Length}");
return res;
}
public void Release()
{
this._AssetBundle.Unload(true);
_ABManifest = null;
_Dependences.Clear();
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace ABFW
{
///
/// AB框架的管理容器
/// 是用来管理AB包依赖的,所以不可能出现循环引用包关系。
///
public class ABOLGraph
{
public class VexNode
{
public ABLoader Loader;
public ArcNode FirstIn; //指向第一个入度
public ArcNode FirstOut;//指向第一个出度
public VexNode(ABLoader loader, ArcNode firstin = null, ArcNode firstout = null)
{
this.Loader = loader;
this.FirstIn = firstin;
this.FirstOut = firstout;
}
}
public class ArcNode
{
public string TailABName; //弧尾AB包的名称
public string HeadABName; //弧头AB包的名称,出度根据弧头获得值
public ArcNode HLink; //指向同弧头的弧
public ArcNode TLink; //指向同弧尾的弧
public ArcNode(string tail, string head, ArcNode hlink = null, ArcNode tlink = null)
{
this.TailABName = tail;
this.HeadABName = head;
this.HLink = hlink;
this.TLink = tlink;
}
}
Dictionary<string, VexNode> _DicVexs;
public ABOLGraph()
{
_DicVexs = new Dictionary<string, VexNode>();
}
///
/// 查询是否存在这样一个结点
///
///
public bool ContainVexNode(string abName)
{
return _DicVexs.ContainsKey(abName);
}
///
/// 获得一个结点
///
///
///
public VexNode GetVexNode(string abName)
{
if (ContainVexNode(abName))
return _DicVexs[abName];
Debug.LogError("没有缓存这样的节点,无法获得");
return null;
}
///
/// 给图中添加一个结点
///
public void AddVexNode(string abName, VexNode vex)
{
if (_DicVexs.ContainsKey(abName))
{
Debug.LogWarning($"内存中已经存在{abName}还未释放!AddVexNode");
return;
}
_DicVexs.Add(abName, vex);
}
public void AddVexNode(string abName, ABLoader loader)
{
if (_DicVexs.ContainsKey(abName))
{
Debug.LogWarning($"内存中已经存在{abName}还未释放!AddVexNode");
return;
}
_DicVexs.Add(abName, new VexNode(loader));
}
///
/// 删除一个对应的AssetBundle结点
/// 但是要ASsetBundle的入度为0才可以删除,一个已经被引用了的AssetBundle不能删除
///
///
public bool RemoveVexNode(string abName)
{
if (!_DicVexs.ContainsKey(abName))
{
Debug.LogWarning($"要删除的ab包{abName}还未加载进内存中!RemoveVexNode");
return false;
}
//先删除与这个顶点有关的所有弧
VexNode v = _DicVexs[abName];
if (v.FirstIn != null)
{
Debug.LogError($"{abName}正在被{v.FirstIn.TailABName}等资源包引用中,所以不能卸载");
return false;
}
for (int i = 0; i < _DicVexs.Keys.Count; i++)
{
string anotherAbName = _DicVexs.Keys.ElementAt(i);
Debug.Log($"RemoveVExNode测试:{anotherAbName}");
if (anotherAbName == abName)
continue;
else
{
RemoveArc(abName, anotherAbName); //删除出弧
}
}
_DicVexs.Remove(abName);
Debug.Log("删除完成");
return true;
}
///
/// 要确保对应的ab包都已经加载!!!
/// 添加图的边关系
/// ab包1->ab包2(ab包1依赖于ab包2)
///
/// ab包1
/// ab包2
public void InsertArc(string abName1, string abName2)
{
ArcNode an = new ArcNode(abName1, abName2);
VexNode v1 = _DicVexs[abName1];
VexNode v2 = _DicVexs[abName2];
if (v1 == null)
{
Debug.Log($"{abName1}包还未加载!InsertEdge");
}
if (v2 == null)
{
Debug.Log($"{abName2}包还未加载!InsertEdge");
}
//构建出度
an.TLink = v1.FirstOut;
v1.FirstOut = an;
//构建入度
an.HLink = v2.FirstIn;
v2.FirstIn = an;
}
///
/// 删除ab1->ab2的弧
///
/// ab包1名称
/// ab包2名称
public void RemoveArc(string abName1, string abName2)
{
VexNode v1 = _DicVexs[abName1];
VexNode v2 = _DicVexs[abName2];
//删除v1的出度
if (v1.FirstOut == null)
{
Debug.LogWarning($"没有由{abName1}->{abName2}这条边!RemoveArc");
return;
}
ArcNode curOut = v1.FirstOut;
if (curOut.HeadABName == abName2)
{
Debug.Log($"找到需要移除的边!{curOut.TailABName}->{curOut.HeadABName}!RemoveArc");
v1.FirstOut = curOut.TLink;
}
else
{
while (curOut.TLink != null && curOut.TLink.HeadABName != abName2)
{
curOut = curOut.TLink;
}
if (curOut.TLink == null)
{
Debug.LogWarning($"没有由{abName1}->{abName2}这条边!RemoveArc");
return;
}
Debug.Log($"找到需要移除的边!{curOut.TailABName}->{curOut.HeadABName}!RemoveArc");
curOut.TLink = curOut.TLink.TLink;
}
//删除v2的入度
ArcNode curIn = v2.FirstIn;
if (curIn.TailABName == abName1)
{
v2.FirstIn = curIn.HLink;
}
else
{
while (curIn.HLink != null && curIn.HLink.TailABName != abName1)
{
curIn = curIn.HLink;
}
curIn.HLink = curIn.HLink.HLink;
}
}
///
/// 完全释放资源
///
public void ClearAll()
{
foreach (var kv in _DicVexs)
{
kv.Value.Loader.Release();
}
_DicVexs.Clear();
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
//打印出所有的出度和入度
foreach (KeyValuePair<string, VexNode> kv in _DicVexs)
{
sb.Append($"{kv.Key}的出度为:");
ArcNode curOUt = kv.Value.FirstOut;
while (curOUt != null)
{
sb.Append($"{curOUt.HeadABName},");
curOUt = curOUt.TLink;
}
sb.Append("NULL");
sb.Append("入度为:");
ArcNode curIn = kv.Value.FirstIn;
while (curIn != null)
{
sb.Append($"{curIn.TailABName},");
curIn = curIn.HLink;
}
sb.AppendLine("NULL");
}
return sb.ToString();
}
}
}
using UnityEngine;
namespace ABFW
{
///
/// 工厂设计模式
///
public class AssetBundleFactory
{
public ABOLGraph _Graph;
private ABManifestLoader _ABManifestLoader; //会放在游戏结束
public AssetBundleFactory()
{
//在PersistentDataPath里面看看有没有对应的AssetBundle,配置文件信息
AssetBundle ab;
string manifestPath = ABDefine.GetABPackPath();
Debug.Log(manifestPath);
ab = AssetBundle.LoadFromFile(manifestPath);
//初始化Manifest
_ABManifestLoader = new ABManifestLoader(ab);
//初始化图
_Graph = new ABOLGraph();
}
///
/// 无依赖AssetBundle包的加载方式
///
///
///
private ABLoader GetSingleABLoader(string abName, bool isPersistent)
{
AssetBundle ab = AssetBundle.LoadFromFile(ABDefine.GetABPackPath(abName));
if (ab == null)
{
Debug.LogError("没有可以加载的对应的AssetBundle,名称为:" + abName);
return null;
}
ABLoader loader = new ABLoader(ab, abName, isPersistent); //不加缓存
return loader;
}
///
/// 对外获得Assetbundle的方法,可能有依赖
///
///
///
public ABLoader GetABLoader(string abName, bool isPersistent = false)
{
//查看缓存中是否拥有
if (_Graph.ContainVexNode(abName))
{
return _Graph.GetVexNode(abName).Loader;
}
//先加载abName的依赖包
string[] dependences = _ABManifestLoader.GetDependences(abName);
for (int i = 0; i < dependences.Length; i++)
{
Debug.Log($"{abName}的依赖{dependences[i]}");
GetABLoader(dependences[i]);
}
//加载abName Loader
ABLoader loader = GetSingleABLoader(abName, isPersistent);
_Graph.AddVexNode(abName, loader);
//构建弧的关系
for (int i = 0; i < dependences.Length; i++)
{
_Graph.InsertArc(abName, dependences[i]); //出度关系
}
return loader;
}
///
/// 卸载某一个AssetBundle,是否卸载完全
///
///
public void ReleaseABLoader(string abName)
{
if (!_Graph.ContainVexNode(abName))
{
Debug.LogWarning($"并未加载这样的AssetBundle{abName},无法释放其资源");
return;
}
ABLoader loader = _Graph.GetVexNode(abName).Loader;
if (_Graph.RemoveVexNode(abName))
{
loader.Release();
//Resources卸载
Resources.UnloadUnusedAssets();
}
}
///
/// 卸载所有的AssetBundle,完全卸载
///
public void ReleaseAllABLoader()
{
_Graph.ClearAll();
//Resources卸载
Resources.UnloadUnusedAssets();
}
}
}
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace ABFW
{
///
/// 泛型方法生成资源
/// 先看资源是否存在对应的AB包中
/// 不存在从Resources中加载
/// 存在获得对应的AB包然后加载
///
public class Asset
{
private static Asset _Instance;
public static Asset Instance
{
get
{
if (_Instance == null)
_Instance = new Asset();
return _Instance;
}
}
private Dictionary<string, string> _ResPath; //key - 资源名称 , value - ab名称
public AssetBundleFactory Factory;
public Asset()
{
Factory = new AssetBundleFactory();
//初始化AB资源集合
_ResPath = new Dictionary<string, string>();
ABIni ini = JsonConvert.DeserializeObject<ABIni>(File.ReadAllText(ABDefine.GetIniPath()));
Debug.Log("资源与AB包对应关系如下:");
for (int i = 0; i < ini.Datas.Count; i++)
{
_ResPath.Add(ini.Datas[i].ResName, ini.Datas[i].ABName);
Debug.Log($"{ini.Datas[i].ResName}->{ini.Datas[i].ABName}");
}
}
///
/// 加载资源
///
///
///
///
public T LoadAsset<T>(string resName) where T : UnityEngine.Object
{
T t;
if (!_ResPath.ContainsKey(resName))
{
//从Resouces下加载
t = Resources.Load<T>(resName);
return t;
}
//从AB中加载
string abName = _ResPath[resName];
ABLoader loader = Factory.GetABLoader(abName);
//对于场景,把ab包加载进内存就足够了。
if (abName.EndsWith(".u3d"))
{
return null;
}
t = loader.LoadAsset<T>(resName);
if (!t)
{
Debug.LogError($"找不到这样的资源文件!{resName}");
return null;
}
return t;
}
///
/// 卸载某个资源
///
///
public void ReleaseResources(string resName)
{
if (_ResPath.ContainsKey(resName))
{
string abname = _ResPath[resName];
ABLoader loader = Factory.GetABLoader(abname);
loader.UnloadResource(resName);
}
}
///
/// 卸载所有的资源
///
///
public void ReleaseAllResources()
{
Factory.ReleaseAllABLoader();
}
}
}