AssetBundle框架管理

自己写的AssetBundle框架设计,用十字链表来缓存已加载的ab,网上还有很多管理方式,比如引用计数,很多算法搞得很复杂无非也就是为了让Assetbundle根据引用计数的情况决定是否释放。如果A引用了B包,在A包还在内存的情况下,就不能卸载B包。构建出AB包之后可以直接把源素材删掉。资源卸载看这个
看上面那个链接,也就是说在从ab包加载出来的prefab被场景引用(实例化)或者代码引用(缓存)的时候,所使用到的贴图材质等ab包就不能被卸载,否则ab包就可以unload true了。
我看那些使用资源引用计数的人也是用了字典来缓存所有的ab包,每个ab包还都对应一个int值来计算引用计数的数量,我每次都加载资源的时候都会保留资源引用,根据卸载资源的引用来判断引用是否为0,为0的时候也会卸载当前ab包,至于被这个ab包所依赖,没有其他依赖的时候的其他ab包也不急着卸载,因为场景还有实例化的物体的时候材质包如果没了,也会出问题。
我也不知道对不对,也没个大神发个资源技术的框架源码教程来瞧瞧…

文章目录

  • 全局路径定义
  • 配置文件
  • IABLoader
  • ABLoader
  • ABManifestLoader
  • 管理容器 十字链表
  • 加载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();
        }
    }
}

IABLoader

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();
    }
}

ABLoader

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);
        }
    }
}

ABManifestLoader

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();
        }
    }
}

加载AB包的工厂

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();
        }
    }
}

使用

AssetBundle框架管理_第1张图片
AssetBundle框架管理_第2张图片
场景中实例化的引用无法监控,ab包和内存中的缓存可以
AssetBundle框架管理_第3张图片
AssetBundle框架管理_第4张图片

你可能感兴趣的:(Unity)