zoukankan      html  css  js  c++  java
  • unity游戏框架学习-资源加载模块

    概述:https://www.cnblogs.com/wang-jin-fu/p/10975660.html

    本篇我们实现unity里的加载模块,他的主要功能是,业务传入资源名字和资源类型,加载模块加载到对应的资源后返回给业务,业务不需要关心该资源是从本地加载还是从AssetBundle里加载。

    加载模块分两部分1.各资源的加载器,例如ab包加载器、Asset加载器、网络下载。2.各加载器的管理类,提供给业务的接口都在这里

    需要支持的能力

    1.能切换不同加载模式 开发阶段编辑器运行直接加载资源无需打ab包,测试或正式发布阶段通过ab包加载资源

    2.缓存机制 能定时清理长时间未使用的资源内存

    3.既有同步加载 也有异步加载

    复杂点:

    1.根据业务传入的资源名字,获取到editor路径、ab包名字。需要事先根据资源名字保存资源的路径、ab包路径配置。

    2.ab包的引用计数维护:加载时ReferencedCount+1,卸载时ReferencedCount-1。

    两种引用:AB包之间的相互依赖,ab包加载时,依赖包引用计数加1,ab包卸载时,依赖包引用减1。2.资源引用,例如使用AssetBundle.LoadAsset加载资源时,该ab包引用计数加一,引用对象被删除时,引用计数减1.

    问题是如何确保被删除的引用对象引用计数能正确减少。

    有两种方式:

      1.纯引用计数。ab包依赖和asset引用都使用引用计数。asset引用类型大概以下几种

      1-1.预制体,额外封装一层,所有预制体的生成和销毁都由一个管理类统一管理。

    例如封装一个ResourceItem类,所有预制体的生成和销毁都必须走这个类的的Create和Dispose类,在ctor方法里加载ab包、实例化预制体,在Dispose方法里Distory对象、卸载ab包(这里的加、卸载只是引用计数加1、减1)。需要业务手动的释放调用Dispose释放对象。

    -- 资源
    -- 所有非UI的预制加载
    local ResourceItem = class("ResourceItem", ObjectBase)
    
    -- 静态创建ResourceItem接口
    -- path Data目录以下,预制的路径
    function ResourceItem.Create(target, filepath, parent, onLoaded, async)
        local abpath = PubFunc.GetAbNameOfPath(filepath)
        local name =  PubFunc.GetNameFromPath(filepath)
        local item = ResourceItem.new(filepath, abpath, name, parent, onLoaded, async)
        target:AddSubItem(item)
        return item
    end
    
    function ResourceItem.CreateUIItem(target, abpath, name, parent, onLoaded, async)
        local filepath = abpath
        if string.find(abpath, name)==nil then
            filepath = abpath..name
        end
    
        local item = ResourceItem.new(filepath, abpath, name, parent, onLoaded, async)
        target:AddSubItem(item)
        return item
    end
    
    function ResourceItem:ctor(filepath, abpath, name, parent, onLoaded, async)
        ResourceItem.super.ctor(self)async = async and true or false
    
        self._filepath  = filepath
        self._path         = abpath         -- 文件路径
        self._name         = name
        self._parent    = parent
        self._onLoaded     = onLoaded     -- 加载完回调
    
        self.gameObject         = nil         --外部可直接获取
        self.transform             = nil
    
        self._loadKey = me.modules.resource:CreateAsyn(abpath, name, handler(self, self.OnLoadComplete), async)
    end-- 清理
    function ResourceItem:Dispose()
        if self.gameObject then
            -- 销毁
            me.modules.resource:Delete(self.gameObject)
            self.gameObject = nil
            self.transform = nil
        elseif self._loadKey then
            -- 取消加载
            me.modules.resource:CancelLoad(self._loadKey)
            self._loadKey = nil
        end
    
        ResourceItem.super.Dispose(self)
    end-- 是否已加载
    function ResourceItem:IsLoaded()
        return self.gameObject ~= nil
    end
    
    -- 加载完成回调
    function ResourceItem:OnLoadComplete(go)
        self._loadKey    = nil
        local trans = nil
    
        if go then
            trans = go.transform
            if self._parent then
                go:SetParent(self._parent)
            end
            trans:SetLocalPositionZero()
            trans:SetLocalScaleOne()
        else
            printError("加载RedourceItem失败,path:", self._path)
        end
        self.gameObject = go
        self.transform = trans
    
        -- 回调给外部
        if self._onLoaded then
            self._onLoaded(self)
        end
    end
    
    return ResourceItem

    你也可以在每个实例化的GameObject上挂在一个脚本,并在该脚本的Destory方法里卸载ab包的引用  

    1-2.场景类,这个比较简单,场景管理类肯定会记录当前的场景信息,在加载新场景时,先卸载当前的ab包就可以了。

    1-3.sprite类,sprite是给image使用的,那么我们可以扩展一下Image的类。例如业务传入图片的名字,ImageEx类根据名字到LoadModule加载对应的ab及sprite并记录当前的sprite名字,当业务下次设置图片或Image对象被Destory时,根据保存的sprite名字卸载ab包。  

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using Debugger = LuaInterface.Debugger;
    
    /// <summary>
    /// image扩展,提供通过图片名字加载图片、从ab包、url、高清资源下载、emoji加载图片接口
    /// TP形Image. 
    /// </summary>
    public class ImageEx : Image
    {
        private string m_SpriteName = "";
        public string SpriteName
        {
            get
            {
                return m_SpriteName;
            }
            set
            {
                m_SpriteName = value;
            }
        }protected override void OnDestroy()
        {
            // 销毁的时候要卸载一下ab
            UnloadSprite();
            StopCurrLoadingUrl();
        }
    
        private void UnloadSprite()
        {
            if (!string.IsNullOrEmpty(SpriteName) && sprite != null)
            {
                SpriteModule.Instance.UnloadSpriteByName(SpriteName);
                SpriteName = null;
                this.sprite = null;
            }
        }public void SetSpriteName(string name)
        {
            if (sprite != null && SpriteName == name)
            {
                return;
            }
    
            UnloadSprite();
            
            if(string.IsNullOrEmpty(name))
            {
                this.sprite = null;
                return;
            }
    
            SpriteName = name;
            SpriteModule.Instance.LoadSpriteByName(name, onLoadedSprite);
        }private void onLoadedSprite(object obj)
        {
            Sprite sp = obj as Sprite;
            this.sprite = sp;
            if (sp == null)
            {
                Debugger.LogError("Load sprite null, name:{0}", m_SpriteUrl);
            }
            else
            {
                if(!string.IsNullOrEmpty(m_strHdResName))
                {
                    sprite.name = m_strHdResName;
                }
            }
        }
    }

      1-4.shader类:全局就一个ab包,常驻内存就好了

      1-5.音乐类:PlayMusic加载、StopMusic卸载就好了

    // 背景音乐
        // fromResources 是否从Resources文件夹下加载 
        public void PlayMusic(string name, bool fromResources = false)
        {
            if (string.IsNullOrEmpty(name)) return;
    
            if (fromResources)
            {
                if(MusicMute)
                {
                    return;
                }
                AudioClip clip = Resources.Load<AudioClip>(name);
                if(clip != null)
                {
                    m_musicSource.enabled = true;
                    m_musicSource.clip = clip;
                    m_musicSource.loop = true;
                    m_musicSource.Play();
                }
            }
            else
            {
                string strBundleName = "sound/music/" +name;
                LoadModule.Instance.LoadAssetFromBundle(strBundleName, name, typeof(AudioClip), (data) => {
                    m_musicSource.enabled = true;
                    m_musicSource.clip = data as AudioClip;
                    m_musicSource.loop = true;
                    m_musicSource.Play();
                });
            }
        }
    
        /// <summary>
        /// 停止音乐并清理
        /// </summary>
        public void StopMusic()
        {
            AudioClip m_musicClip = m_musicSource.clip;
            if (m_musicClip)
            {
                m_musicSource.Stop();
                m_musicSource.clip = null;
    
                string currentMusicName = m_musicClip.name;
                AssetBundleCache assetBundleCache = ABCachePool.Instance.GetABCacheByName(string.Format("sound_music_{0}.unity3d", currentMusicName));
                if (assetBundleCache != null)
                {
                    assetBundleCache.ReferencedCount = 1;
                    LoadModule.Instance.UnloadAssetBundle(string.Format("sound/music/{0}", currentMusicName), true);
                }
            }
        }  

      2.引用计数+弱引用。ab包依赖使用引用计数,asset引用使用弱引用,业务在加载asset时需要传入引用的对象(实例化就不用了,可以把实例化出来的GameObject当作引用对象),通过判断对象是否为空来判断引用关系。

      强引用:我们实例化一个对象,直接引用了这个对象就是强引用。在这个对象被强引用的时,GC无法回收这个对象。只有当该对象所有的强引用都失去的时候,GC才会回收该对象。

      弱引用:弱引用可以保持对对象的引用,同时允许GC在必要时释放对象,回收内存。这边一定要用弱引用,不然会影响对象的回收。

    protected List<System.WeakReference> mReferenceOwnerList;
    /// <summary>
    /// 为AB添加指定owner的引用
    /// 所有owner都销毁则ab引用计数归零可回收
    /// </summary>
    /// <param name="owner"></param>
    protected void retainOwner(UnityEngine.Object owner)
    {
       if (owner == null)
       {
           ResourceLogger.logErr(string.Format("引用对象不能为空!无法为资源:{0}添加引用!", AssetBundleName));
           return;
        }
    
        foreach (var referenceowner in mReferenceOwnerList)
        {
            if (owner.Equals(referenceowner))
            {
                return;
            }
        }
    
        System.WeakReference wr = new System.WeakReference(owner);
        mReferenceOwnerList.Add(wr);
    }
    /// <summary>
    /// 获取AB有效的引用对象计数
    /// </summary>
    /// <returns></returns>
    protected int updateOwnerReference()
    {
       for (int i = 0; i < mReferenceOwnerList.Count; i++)
       {
            UnityEngine.Object o = (UnityEngine.Object)mReferenceOwnerList[i].Target;
            if (!o)
            {
                mReferenceOwnerList.RemoveAt(i);
                i--;
            }
        }
        return mReferenceOwnerList.Count;
    }

    第一种方式需要业务手动Dispose无用的对象,当然这是个好习惯,需要框架严格注意引用对象的管理。第二种需要业务在引用时传入引用的对象,需要额外的参数。

    一个加载模块大致结构如下:

    加载模块结构如上图,load为加载器,ResManager为提供给业务调用的接口,LoadModule为不使用ab包的加载接口,ABLoadModule为使用ab包的加载接口,这两个module对用户封闭。

    AssetLoader:在editor模式下加载资源。AssetBundleLoader :ab包加载器,负责从内存加载AssetBundle。BundleAssetLoader :负责从指定的ab包加载资源。AssetBundleCache:缓存的ab包。

    一、加载器实现

    上篇我们有说到,unity有四种加载方式

    1.AssetDatabase:在编辑器内加载卸载资源,并不能在游戏发布时使用,它只能在编辑器内使用。但是,它加载速度快,使用简单。

    2.Resources:因为使用Resources文件夹无法热更,本片篇就不实现此途径了。

    3.AssetBundle:参考https://www.cnblogs.com/wang-jin-fu/p/11171626.html,支持热更,但是每次资源变化都得重新打ab包(奇慢),所以适合发布模式,但开发模式千万别用。

    4.UnityWebRequest:从网络端下载

    1.所有的加载器都继承自一个接口:Loader,该类定义了当前的加载类型、初始化、回收的重置方法、加载方法、加载进度回调等

    public class Loader
    {
        #region Define
        public enum LoaderType
        {
            STREAM,            // 流(原则上可以是任何文件,包括远程服务器上的)
            ASSET,            // Asset目录下的资源
            BUNDLE,            // AssetBundle
            BUNDLEASSET,    // AssetBundle中的资源
            SCENE,          // 场景
            Texture,        // 图片
        }
    
        public enum LoaderState
        {
            NONE,            // 
            LOADING,        // 加载中
            FINISHED,        // 完成
        }
    
        public delegate void ProgressHandle(Loader loader, float rate);
        public delegate void LoadedHandle(Loader loader, object data);
        #endregion
    
        protected Loader(LoaderType type)
        {
            m_type = type;
        }
    
        protected LoaderType m_type;            // 加载器类型
        protected LoaderState m_state;            // 加载状态
        protected string m_path;                // 路径
        protected bool m_async;                    // 是否异步
    
        protected ProgressHandle m_onProgress;    // 加载进度
        protected LoadedCallback m_onloaded;    // 加载完成回调通知
    
        protected System.Diagnostics.Stopwatch m_watch = new System.Diagnostics.Stopwatch ();//加载时间统计
    
        public LoaderType Type { get { return m_type; } }
        public string Path { get { return m_path; } }
        public bool IsFinish { get { return m_state == LoaderState.FINISHED; } }
        public bool IsAsync { get { return m_async; } }
    
      //主要用于ab包的判断,因为ab包需要等待依赖包的加载
    public virtual bool IsPrepareToLoad() { return true; } //初始化参数 public virtual void Init(string path, LoadedCallback onloaded, bool async = true) { m_state = LoaderState.NONE; m_path = path; m_async = async; m_onloaded = onloaded; } public virtual void Reset() { m_path = ""; m_async = true; m_onloaded = null; m_state = LoaderState.NONE; m_onProgress = null; } public virtual void Load() { m_watch.Reset (); m_watch.Start (); m_state = LoaderState.LOADING; OnLoadProgress(0f); } public virtual void Stop() { Reset(); } public virtual void Update(float dt) { } protected virtual void OnLoadProgress(float rate) { if (m_onProgress != null) { m_onProgress(this, rate); } } protected virtual void OnLoadCompleted(object data) { m_state = LoaderState.FINISHED; try { if (m_onloaded!= null) { m_onloaded (data); } } catch(System.Exception e) { LuaInterface.Debugger.LogError(e.Message); } OnLoadProgress(1f); } }

    2.editor模式下的加载,直接使用AssetDatabase加载。

    public class AssetLoader : Loader
    {
        Object m_data = null;
        System.Type m_assetType = null;        //资源类型
    
        public AssetLoader()
            : base(Loader.LoaderType.ASSET)
        {
    
        }
    
        public void Init (string path, System.Type type, LoadedCallback onLoaded, bool async = true)
        {
            base.Init (path, onLoaded, async);
            m_assetType = type;
        }
    
        public override void Load()
        {
            base.Load();
    
    #if UNITY_EDITOR
            if (m_assetType == null)
            {
                m_assetType = typeof(Object);
            }
    
            m_data = UnityEditor.AssetDatabase.LoadAssetAtPath(m_path, m_assetType);
            if (!m_async)
            {
                OnLoadCompleted(m_data);
            }
    #else
            if(!m_async)
            {
                OnLoadCompleted(null);
            }
    #endif
        }
    
        public override void Update(float dt)
        {
            if (m_state == LoaderState.LOADING)
            {
                OnLoadCompleted(m_data);
                m_data = null;
            }
        }
    }

    3.ab包的缓存可以参考我之前的文章:ab包的缓存

    ab加载如下:当且仅当IsPrepareToLoad判断通过(即所有依赖包都加载完成)才能调用load方法,开始ab包的加载。InitDependencies方法用于初始化当前ab包的依赖项

    load加载分两种,第一种是从扩展包加载,第二种是从本地加载。

    ab包的加载无非就是同步和异步加载的区别,ab包的卸载也只需要调用Unload方法就好了。唯一需要注意的是,记载asset前必须保证ab的依赖包都加载完成了。

    AssetBundleCache:缓存类,用于缓存ab包,提供从ab包加载asset的方法并缓存a包

    public class AssetBundleCache
    {
        private string m_name;          // AssetBundle name
        private int m_referencedCount;  // 引用计数
        private float m_unloadTime;     // 释放时间
    
        private HashSet<string> m_setAllAssetNames = null;//ab包包含的所有asset的名字,用于判断指定asset是否存在于ab中
        private Dictionary<string, AssetBundleRequest> m_dicAsync = new Dictionary<string, AssetBundleRequest>();//正在异步加载的asset
        private Dictionary<string, Object> m_dicAssetCache = null;//已经加载完的asset
    
        public AssetBundleCache(string name, AssetBundle ab, int refCount)
        {
            m_name = name;
            Bundle = ab;
            ReferencedCount = refCount;
        }
    
        // AssetBundle
        public AssetBundle Bundle
        {
            get;
            private set;
        }
    
        // 是否常驻,通用资源的ab包不卸载
        public bool Persistent
        {
            get;
            set;
        }
    
        public string BundleName
        {
            get
            {
                return m_name;
            }
        }
    
        // 引用计数
        public int ReferencedCount
        {
            get
            {
                return m_referencedCount;
            }
    
            set
            {
                m_referencedCount = value;
                if (m_referencedCount <= 0)
                {
                    m_unloadTime = Time.realtimeSinceStartup;
                }
                else
                {
                    m_unloadTime = 0;
                }
                if (m_referencedCount < 0)
                {
                    Debug.LogWarningFormat("AssetBundleCache reference count < 0, name:{0}, referencecount:{1}", m_name, m_referencedCount);
                }
            }
        }
    
        // 是否可以删除
        public bool IsCanRemove
        {
            get
            {
                // 常驻资源
                if (Persistent) return false;
    
                // 非常驻,并且引用计数为0
                if (!Persistent && ReferencedCount <= 0)
                {
                    return true;
                }
    
                return false;
            }
        }
    
        // 缓存时间到
        public bool IsTimeOut
        {
            get
            {
                return Time.realtimeSinceStartup - m_unloadTime >= Config.Instance.AssetCacheTime;
            }
        }
    
        // 资源是否正在异步加载中
        public bool IsAssetLoading(string name)
        {
            return m_dicAsync.ContainsKey(name);
        }
    
        //判断AB是否包含某个资源
        public bool IsExistAsset(string strAssetName)
        {
            if (m_setAllAssetNames != null && m_setAllAssetNames.Contains(strAssetName))
            {
                return true;
            }
            return false;
        }
    
        // 获取缓存中的资源
        public Object GetCacheAsset(string name)
        {
            if (m_dicAssetCache != null)
            {
                Object rst = null;
                if (m_dicAssetCache.TryGetValue(name, out rst))
                {
                    return rst;
                }
            }
            return null;
        }
    
        // 资源加载完 要缓存一下
        public void OnLoadedAsset(string name, Object asset)
        {
            m_unloadTime = Time.realtimeSinceStartup;
    
            if (m_dicAsync.ContainsKey(name))
            {
                m_dicAsync.Remove(name);
            }
    
            // 常驻ab加载进来的资源 用真实引用 不用弱引用
            if (m_dicAssetCache == null)
            {
                m_dicAssetCache = new Dictionary<string, Object>();
            }
            if (m_dicAssetCache.ContainsKey(name))
            {
                Debug.LogWarningFormat("警报! 缓存已经存在了还重新加载:{0}", name);
            }
            m_dicAssetCache[name] = asset;
            return;
        }
    
        //异步加载资源,需要添加到m_dicAsync里,防止重复加载
        public AssetBundleRequest LoadAssetAsync(string name, System.Type type)
        {
            if (Bundle == null)
            {
                Debug.LogWarningFormat("AssetBundle:{0} is null, load asset async:{1},type:{2}, fail!!", m_name, name, type.ToString());
                return null;
            }
            AssetBundleRequest opt;
            m_dicAsync.TryGetValue(name, out opt);
            if (opt == null)
            {
                opt = Bundle.LoadAssetAsync(name, type);
                m_dicAsync.Add(name, opt);
            }
            return opt;
        }
    
        //加载资源
        public Object LoadAsset(string name, System.Type type)
        {
            if (Bundle == null)
            {
                Debug.LogWarningFormat("AssetBundle:{0} is null, load asset:{1},type:{2}, fail!!", m_name, name, type.ToString());
                return null;
            }
    
            Object asset = Bundle.LoadAsset(name, type);
            if (asset == null)
            {
                Debug.LogWarningFormat("AssetBuncleCache.LoadAsset, asset not exist:{0}, {1}", m_name, name);
            }
            else
            {
                OnLoadedAsset(name, asset);
            }
    
            return asset;
        }
    
        //加载所有资源
        public UnityEngine.Object[] LoadAllAssets(bool bCache = true)
        {
            UnityEngine.Object[] allObjs = Bundle.LoadAllAssets();
            if (bCache)
            {
                for (int i = 0; i < allObjs.Length; i++)
                {
                    Object obj = allObjs[i];
                    OnLoadedAsset(obj.name, obj);
                }
            }
    
            return allObjs;
        }
    
        //异步加载所有资源 只用作预加载使用
        public AssetBundleRequest LoadAllAssetsAsync()
        {
            if (Bundle == null)
            {
                return null;
            }
            return Bundle.LoadAllAssetsAsync();
        }
    
        public bool LoadAllAssetNames()
        {
            if (Bundle == null)
            {
                return false;
            }
            string[] arrNames = Bundle.GetAllAssetNames();
            if (arrNames.Length == 0)
            {
                return false;
            }
            if (m_setAllAssetNames == null)
            {
                m_setAllAssetNames = new HashSet<string>();
            }
    
            for (int i = 0; i < arrNames.Length; i++)
            {
                string strName = System.IO.Path.GetFileNameWithoutExtension(arrNames[i]);
                m_setAllAssetNames.Add(strName);
            }
            return true;
        }
    
        //卸载ab包
        public void Unload()
        {
            if (m_dicAsync.Count > 0)
            {
                Debug.LogWarningFormat("[仅提醒]该Bundle还有资源在加载中,暂时不卸载:{0}", m_name);
                return;
            }
    
            if (Bundle != null)
            {
    
                Bundle.Unload(false);
                Bundle = null;
            }
    
            if (m_setAllAssetNames != null)
            {
                m_setAllAssetNames.Clear();
            }
    
            if (m_dicAssetCache != null)
            {
                m_dicAssetCache.Clear();
            }
        }
    }

    ABCachePool:负责管理ab包的引用计数、缓存、获取。

    public class ABCachePool
    {
        #region Instance
        private static ABCachePool m_Instance;
        public static ABCachePool Instance
        {
            get { return m_Instance ?? (m_Instance = new ABCachePool()); }
        }
        #endregion
        Dictionary<string, AssetBundleCache> m_AssetBundleCaches = new Dictionary<string, AssetBundleCache>();  // 缓存队列
        HashSet<string> m_persistentABs = new HashSet<string>();
    
        public Dictionary<string, AssetBundleCache> AssetBundleCaches
        {
            get
            {
                return m_AssetBundleCaches;
            }
        }
    
        public void ClearAllCache()
        {
            foreach (KeyValuePair<string, AssetBundleCache> keyval in m_AssetBundleCaches)
            {
                keyval.Value.Unload();
            }
            m_AssetBundleCaches.Clear();
        }
    
        public bool IsExistCache(string abName)
        {
            return m_AssetBundleCaches.ContainsKey(abName);
        }
    
        // 引用这个bundle
        public AssetBundleCache ReferenceCacheByName(string abName)
        {
            AssetBundleCache cache = null;
            m_AssetBundleCaches.TryGetValue(abName, out cache);
            if (cache != null)
            {
                ++cache.ReferencedCount;
            }
            return cache;
        }
    
        // 获取ABCache 不增加引用
        public AssetBundleCache GetABCacheByName(string abName)
        {
            AssetBundleCache cache = null;
            m_AssetBundleCaches.TryGetValue(abName, out cache);
            return cache;
        }
    
        public AssetBundleCache AddCache(string abName, AssetBundle bundle, int refCount)
        {
            if (m_AssetBundleCaches.ContainsKey(abName))
            {
                Debug.LogWarningFormat("AssetBundleCache already contains key:{0}, it will be cover by new value.", abName);
            }
    
            AssetBundleCache cache = new AssetBundleCache(abName, bundle, refCount);
            m_AssetBundleCaches[abName] = cache;
    
            if (m_persistentABs.Contains(abName))
            {
                cache.Persistent = true;
            }
    
            return cache;
        }
    
        // immediate 只有场景是立刻卸载
        public AssetBundleCache UnReferenceCache(string abName, bool immediate = false)
        {
            AssetBundleCache cache = null;
            if (!m_AssetBundleCaches.TryGetValue(abName, out cache))
            {
                return null;
            }
    
            if (cache.Persistent)
            {
                return null;
            }
    
            --cache.ReferencedCount;
    
            if (immediate && cache.IsCanRemove)
            {
                RemoveCache(abName);
            }
    
            return cache;
        }
    
        public void RemoveCache(string abName)
        {
            AssetBundleCache cache = m_AssetBundleCaches[abName];
            cache.Unload();
            m_AssetBundleCaches.Remove(abName);
        }
    
        private List<string> m_lstRm = new List<string>();
        // 清除无引用的AssetBundle缓存
        public void ClearNoneRefCache(bool mustTimeout)
        {
            foreach (KeyValuePair<string, AssetBundleCache> keyval in m_AssetBundleCaches)
            {
                AssetBundleCache item = keyval.Value;
                // 只清除引用计数为0的
                if (item.IsCanRemove && (!mustTimeout || item.IsTimeOut))
                {
                    m_lstRm.Add(keyval.Key);
                }
            }
    
            for (int i = 0; i < m_lstRm.Count; i++)
            {
                RemoveCache(m_lstRm[i]);
            }
            m_lstRm.Clear();
        }
    
        /// <summary>
        /// 常驻ab包设置
        /// </summary>
        /// <param name="arrAB"></param>
        public void SetPersistentABs(string[] arrAB)
        {
            m_persistentABs.Clear();
            for (int i = 0; i < arrAB.Length; i++)
            {
                string strAB = arrAB[i];//FileHelper.GenBundlePath(arrAB[i]);
                m_persistentABs.Add(strAB);
    
                AssetBundleCache abCache;
                m_AssetBundleCaches.TryGetValue(strAB, out abCache);
                if (abCache != null)
                {
                    abCache.Persistent = true;
                }
            }
        }
    }

    BundleLoader:用于加载AssetBundle

    public class BundleLoader : Loader
    {
        AssetBundleCreateRequest m_abRequest = null;
    
        private int m_iRefCount;                // 当前等待此加载的引用计数
        private List<string> m_lstDepAbNames = new List<string>();  //依赖的未加载Bundle名字列表
    
        private LoadedCallback m_onABLoaded;
    
        private string m_strBundleName;         // 相对路径
        public string BundleName { get { return m_strBundleName; } }
    
        System.Diagnostics.Stopwatch m_saveAbWatcher = new System.Diagnostics.Stopwatch();
        public BundleLoader()
            : base(Loader.LoaderType.BUNDLE)
        {
    
        }
    
        public void AddLoadedCallback(LoadedCallback onloaded)
        {
            m_onABLoaded += onloaded;
            m_iRefCount += 1;
        }
    
        public void AddReferenced()
        {
            m_iRefCount += 1;
        }
    
        public override void Reset()
        {
            base.Reset();
    
            m_iRefCount = 0;
            m_lstDepAbNames.Clear();
            m_strBundleName = "";
    
            m_abRequest = null;
            m_onABLoaded = null;
            m_lstDepAbNames.Clear();
        }
    
        // 判断是否所有依赖都已经加载
        public override bool IsPrepareToLoad()
        {
            for (int i = m_lstDepAbNames.Count - 1; i >= 0; i--)
            {
                string strABName = m_lstDepAbNames[i];
                if (ABCachePool.Instance.IsExistCache(strABName))
                {
                    m_lstDepAbNames.RemoveAt(i);
                }
                else
                {
                    break;
                }
            }
            return m_lstDepAbNames.Count == 0;
        }
    
        public void Init(string path, string strName, string[] dependencies, LoadedCallback onloaded, bool async = true)
        {
            // Bundle 比较特殊 不使用父类的回调
            base.Init(path, null, async);
    
            m_iRefCount = 1;
            m_strBundleName = strName;
            m_onABLoaded = onloaded;
            InitDependencies(dependencies);
        }
    
        private void InitDependencies(string[] dependencies)
        {
            if (dependencies == null || dependencies.Length == 0)
                return;
            for (int i = 0; i < dependencies.Length; i++)
            {
                string strName = dependencies[i];
                if (!ABCachePool.Instance.IsExistCache(strName))
                {
                    m_lstDepAbNames.Add(strName);
                }
            }
        }
    
        public override void Load()
        {
            base.Load();
    
            if (m_async)
            {
                string path = FileHelper.GetAPKPath(m_path);//根据ab包的名字索引ab包的存储路劲
                m_abRequest = AssetBundle.LoadFromFileAsync(path);
            }
            else
            {
                AssetBundle ab = null;
                try
                {
                    // 同步使用AssetBundle.LoadFromFile加载,速度最快
                    if (ab == null)
                    {
                        string path = FileHelper.GetAPKPath(m_path);//根据ab包的名字索引ab包的存储路劲
                        ab = AssetBundle.LoadFromFile(path);
                    }
                }
                catch (System.Exception e)
                {
                    Debug.LogError(e.Message);
                }
                finally
                {
                    OnLoaded(ab);
                }
            }
        }
    
        public override void Update(float dt)
        {
            if (m_state == LoaderState.LOADING)
            {
                if (m_abRequest != null)
                {
                    if (m_abRequest.isDone)
                    {
                        OnLoaded(m_abRequest.assetBundle);
                    }
                    else
                    {
                        DoProgress(m_abRequest.progress);
                    }
                }           
            }
        }
    
        void DoProgress(float rate)
        {
        }
    
        void OnLoaded(AssetBundle ab)
        {
            AssetBundleCache cache = ABCachePool.Instance.AddCache(m_strBundleName, ab, m_iRefCount);
            OnLoadCompleted(ab);
    
            if (m_onABLoaded != null)
            {
                m_onABLoaded(cache);
            }
        }
    }

    二、缓存池,缓存加载器

    public class LoaderPool
    {
        #region Instance
        private static LoaderPool m_Instance;
        public static LoaderPool Instance
        {
            get { return m_Instance ?? (m_Instance = new LoaderPool()); }
        }
        #endregion
    
        List<Loader> m_bundleLoaderPool = new List<Loader>();       // BundleLoader的缓存
        List<Loader> m_assetLoaderPool = new List<Loader>();        // BundleAssetLoader的缓存
    
        public T GetLoader<T>() where T : Loader, new()
        {
            System.Type type = typeof(T);
            T loader = null;
            if (type == typeof(BundleLoader))
            {
                if (m_bundleLoaderPool.Count > 0)
                {
                    loader = (T)m_bundleLoaderPool[0];
                    m_bundleLoaderPool.RemoveAt(0);
                    return loader;
                }
            }
            else if (type == typeof(BundleAssetLoader))
            {
                if (m_assetLoaderPool.Count > 0)
                {
                    loader = (T)m_assetLoaderPool[0];
                    m_assetLoaderPool.RemoveAt(0);
                    return loader;
                }
            }
    
            loader = new T();
    
            return loader;
        }
    
        public void RecycleLoader(Loader loader)
        {
    
            if (loader.GetType() == typeof(BundleLoader))
            {
                loader.Reset();
                m_bundleLoaderPool.Add(loader);
            }
            else if (loader.GetType() == typeof(BundleAssetLoader))
            {
                loader.Reset();
                m_assetLoaderPool.Add(loader);
            }
            else
            {
                loader.Reset();
            }
        }
    }

    三、加载管理器LoadModule

    1.ResManager :提供给业务的接口

    public class ResManager
    {
        #region Instance
        private static ResManager m_Instance;
        public static ResManager Instance
        {
            get
            {
                return m_Instance ?? (m_Instance = new ResManager());
            }
        }
        #endregion
        public int SyncCount = 6;                               // 同步加载并发数
    
        private bool m_isUseAssetBundle;
        private ILoadModule m_loadModule;
    
        public void Init(bool isUseAssetBundle)
        {
            m_isUseAssetBundle = isUseAssetBundle;
            if (isUseAssetBundle)
            {
                m_loadModule = new ABLoadModule();
            }
            else
            {
                m_loadModule = new LoadModule();
            }
    
            m_loadModule.Init(SyncCount);
        }
    
        public void UnInit()
        {
            m_loadModule.UnInit();
        }
    
        public void Update(float dt)
        {
            m_loadModule.Update(dt);
        }
    
        public void Clear()
        {
            ABCachePool.Instance.ClearNoneRefCache(false);
    
            Resources.UnloadUnusedAssets();
        }
    
        public void LoadPrefab(string strPath, string name, LoadedCallback onLoaded, bool async = true)
        {
            m_loadModule.LoadPrefab(strPath, name, onLoaded, async);
        }
    
        public void LoadMusic(string name, LoadedCallback onLoaded, bool async = true)
        {
            m_loadModule.LoadMusic(name, onLoaded, async);
        }
    
        public void LoadFont(string name, LoadedCallback onLoaded, bool async = true, bool inBundle = false)
        {
            m_loadModule.LoadFont(name, onLoaded, async, inBundle);
        }
    
        public void LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded)
        {
            m_loadModule.LoadScene(name, scenePath, isAdditive, onLoaded);
        }
    }

    2.两种加载器的接口类ILoadModule

    public interface ILoadModule
    {
        void Init(int syncCout);
    
        void UnInit();
    
        void Update(float dt);
    
        void LoadPrefab(string strPath, string name, LoadedCallback onLoaded, bool async = true);
    
        void LoadMusic(string name, LoadedCallback onLoaded, bool async = true);
    
        void LoadFont(string name, LoadedCallback onLoaded, bool async = true, bool inBundle = false);
    
        void LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded);
    }

    3.editor模式下的加载器

    /// <summary>
    /// 不使用ab加载资源(Editor模式下)
    /// </summary>
    public class LoadModule : ILoadModule
    {
        public int SyncCount;                               // 同步加载并发数
        public HashSet<string> m_loadedBundleNames = new HashSet<string>();
        // 加载队列
        List<Loader> m_loadings = new List<Loader>();//正在加载
        List<Loader> m_loaderQueue = new List<Loader>();//等待加载
    
        float m_lastClear = 0;  // 上一次清除时间
    
        public void Init(int syncCout)
        {
            SyncCount = syncCout;
        }
    
        public void UnInit()
        {
            for (int i = 0; i < m_loadings.Count; i++)
            {
                m_loadings[i].Stop();
            }
    
            m_loadings.Clear();
            m_loaderQueue.Clear();
        }
    
        private List<int> m_lstRmTemp = new List<int>();
        public void Update(float dt)
        {
            for (int i = m_loadings.Count - 1; i >= 0; i--)
            {
                Loader loader = m_loadings[i];
                loader.Update(dt);
                if (loader.IsFinish)
                {
                    m_loadings.RemoveAt(i);
                    LoaderPool.Instance.RecycleLoader(loader);
                }
            }
    
            int remain = Mathf.Min(SyncCount - m_loadings.Count, m_loaderQueue.Count);
            for (int i = 0; i < m_loaderQueue.Count; i++)
            {
                Loader loader = m_loaderQueue[i];
                m_loadings.Add(loader);
                loader.Load();
                loader.Update(dt);
                m_lstRmTemp.Add(i);
                if (m_lstRmTemp.Count >= remain)
                {
                    break;
                }
            }
    
            if (m_lstRmTemp.Count > 0)
            {
                for (int i = m_lstRmTemp.Count - 1; i >= 0; i--)
                {
                    m_loaderQueue.RemoveAt(m_lstRmTemp[i]);
                }
                m_lstRmTemp.Clear();
            }
        }
    
        public void Clear()
        {
            Resources.UnloadUnusedAssets();
        }
    
        public void LoadPrefab(string strPath, string name, LoadedCallback onLoaded, bool async = true)
        {
            LoadAssetByPath(strPath, name, typeof(GameObject), onLoaded, async);
        }
    
        public void LoadMusic(string name, LoadedCallback onLoaded, bool async = true)
        {
            string path = string.Format("music/{0}", name);
            LoadAssetByPath(path, name, typeof(AudioClip), onLoaded, async);
        }
    
        public void LoadFont(string name, LoadedCallback onLoaded, bool async = true, bool inBundle = false)
        {
            string path = string.Format("ui/font/{0}", name);
            LoadAssetByPath(path, name, typeof(Font), onLoaded, async);
        }
    
        #region LoadScene
        public void LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded)
        {
            var activeSceneName = SceneManager.GetActiveScene().name;
            // 如果当前场景是要加载的场景 直接返回
            if (activeSceneName == name)
            {
                if (onLoaded != null)
                {
                    onLoaded(null);
                }
                return;
            }
    
            if (isAdditive)
            {
                __LoadScene(name, scenePath, isAdditive, onLoaded);
            }
            else //大场景先加载idle过渡
            {
                __LoadScene(name, scenePath, isAdditive, onLoaded);
            }
        }
    
        private void __LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded, bool async = true)
        {
            SceneLoader sLoader = LoaderPool.Instance.GetLoader<SceneLoader>();
            sLoader.Init(name, scenePath, isAdditive, onLoaded, async);
            StartLoad(sLoader, true);
        }
        #endregion
    
        //从Bundle中加载资源
        public void LoadAssetByPath(string path, string name, System.Type type, LoadedCallback onLoaded, bool async = true)
        {
            path = "Assets/Data/" + path;
            if (Directory.Exists(path))
            {
                path += "/" + name;
            }
    
            string suffix = GetSuffixOfAsset(type);
            string fullPath = string.Format("{0}.{1}", path, suffix);
            LoadAsset(fullPath, onLoaded, type, false);
        }
    
        // 加载资源(Assets目录下,带后缀)
        public void LoadAsset(string path, LoadedCallback onLoaded, System.Type type = null, bool async = true)
        {
            if (!File.Exists(path))
            {
                Debug.LogErrorFormat("Load Asset, Path:[{0}] not exist!  ", path);
                if (onLoaded != null)
                {
                    onLoaded(null);
                }
                return;
            }
    
            AssetLoader aLoader = LoaderPool.Instance.GetLoader<AssetLoader>();
            aLoader.Init(path, type, onLoaded, async);
            StartLoad(aLoader, async);
        }
    
        void StartLoad(Loader loader, bool async, bool toHead = false)
        {
            // 异步加载或者加载还未准备好,则当做异步处理,外部控制是否加入队列开头
            // 同步加载,并且已经具备加载条件,则直接调用Load进行加载
            if (async || !loader.IsPrepareToLoad())
            {
                if (toHead)
                {
                    m_loaderQueue.Insert(0, loader);
                }
                else
                {
                    m_loaderQueue.Add(loader);
                }
            }
            else
            {
                m_loadings.Add(loader);
                loader.Load();
            }
        }
    
        // 卸载关卡场景
        public void UnloadLevelScene(string sceneName, bool immediate)
        {
            SceneManager.UnloadSceneAsync(sceneName);
        }
    
        private void CallFunc_LoadedBack(LoadedCallback callback, object data)
        {
            if (callback != null)
            {
                callback(data);
            }
        }
    
        private string GetSuffixOfAsset(System.Type type)
        {
            if (type == typeof(Font))
            {
                return "ttf";
            }
            else if (type == typeof(AudioClip))
            {
                return "ogg";
            }
            else if (type == typeof(GameObject))
            {
                return "prefab";
            }
            else if (type == typeof(TextAsset))
            {
                return "bytes";
            }
            else if (type == typeof(Texture2D) || type == typeof(Sprite))
            {
                return "png";
            }
            return "";
        }
    }

    4.ab包加载模块:和LoadModule 相比,1.ABLoadModule 需要在初始化前加载manifest文件。2.需要在加载资源前加载ab包以及a包的依赖包。3.需要提供卸载ab包的方法。

    /// <summary>
    /// 使用ab加载资源(Editor模式下、线上平台)
    /// </summary>
    public class ABLoadModule : ILoadModule
    {
        public int SyncCount;                               // 同步加载并发数
        private AssetBundleManifest m_manifest = null;
        private HashSet<string> m_bundleNames = new HashSet<string>();
        // 加载队列
        List<Loader> m_loadings = new List<Loader>();//正在加载
        List<Loader> m_loaderQueue = new List<Loader>();//等待加载
        Dictionary<string, AssetBundleLoader> m_dicAllLoader = new Dictionary<string, AssetBundleLoader>();
    
        float m_lastClear = 0;  // 上一次清除时间
    
        public void Init(int syncCout)
        {
            SyncCount = syncCout;
            LoadManifest();
        }
    
        public void UnInit()
        {
            for (int i = 0; i < m_loadings.Count; i++)
            {
                m_loadings[i].Stop();
            }
            m_loadings.Clear();
    
            m_loaderQueue.Clear();
            ABCachePool.Instance.ClearAllCache();
        }
    
        private List<int> m_lstRmTemp = new List<int>();
        public void Update(float dt)
        {
            for (int i = m_loadings.Count - 1; i >= 0; i--)
            {
                Loader loader = m_loadings[i];
                loader.Update(dt);
                if (loader.IsFinish)
                {
                    if (loader.Type == Loader.LoaderType.BUNDLE)
                    {
                        AssetBundleLoader bLoader = loader as AssetBundleLoader;
                        if (m_dicAllLoader.ContainsKey(bLoader.BundleName))
                        {
                            m_dicAllLoader.Remove(bLoader.BundleName);
                        }
                    }
                    m_loadings.RemoveAt(i);
                    LoaderPool.Instance.RecycleLoader(loader);
                }
            }
    
            int remain = Mathf.Min(SyncCount - m_loadings.Count, m_loaderQueue.Count) ;
    
            for (int i = 0; i < m_loaderQueue.Count; i++)
            {
                Loader loader = m_loaderQueue[i];
                if (loader.Type == Loader.LoaderType.BUNDLE)
                {
                    AssetBundleLoader bLoader = loader as AssetBundleLoader;
                    if (!bLoader.IsPrepareToLoad())
                    {
                        continue;   //如果有依赖未加载完直接跳过
                    }
                }
                else if (loader.Type == Loader.LoaderType.BUNDLEASSET)
                {
                    BundleAssetLoader bLoader = loader as BundleAssetLoader;
                    if (!bLoader.IsPrepareToLoad())
                    {
                        continue;   //Asset是否准备好加载
                    }
                }
    
                m_loadings.Add(loader);
                loader.Load();
                loader.Update(dt);
                m_lstRmTemp.Add(i);
                if (m_lstRmTemp.Count >= remain)
                {
                    break;
                }
            }
    
            if (m_lstRmTemp.Count > 0)
            {
                for (int i = m_lstRmTemp.Count - 1; i >= 0; i--)
                {
                    m_loaderQueue.RemoveAt(m_lstRmTemp[i]);
                }
                m_lstRmTemp.Clear();
            }
    
            UpdateAssetBundleCache();
        }
    
        // 更新AssetBundle缓存(主要执行定时清理)
        public void UpdateAssetBundleCache()
        {
            // 每5秒回收一次
            if (Time.realtimeSinceStartup - m_lastClear < 5)
            {
                return;
            }
    
            m_lastClear = Time.realtimeSinceStartup;
    
            /// 检查无引用的AB节点
            ABCachePool.Instance.ClearNoneRefCache(true);
        }
    
        public void Clear()
        {
            ABCachePool.Instance.ClearNoneRefCache(false);
    
            Resources.UnloadUnusedAssets();
        }
    
        #region LoadManifest
        // 加载资源清单
        public void LoadManifest()
        {
            if (m_manifest != null)
            {
                Object.DestroyImmediate(m_manifest, true);
                m_manifest = null;
            }
    
            m_bundleNames.Clear();
    
            string manifestName = FileHelper.MANIFEST_NAME;//manifest文件名
            string strFullPath = FileHelper.SearchFilePath(manifestName);//获取manifest路径
    
            AssetBundleLoader bLoader = LoaderPool.Instance.GetLoader<AssetBundleLoader>();
            bLoader.Init(strFullPath, manifestName, null, delegate (object data) {
                AssetBundleCache ab = data as AssetBundleCache;
                if (ab != null)
                {
                    m_manifest = (AssetBundleManifest)ab.LoadAsset("AssetBundleManifest", typeof(AssetBundleManifest));
                }
    
                ABCachePool.Instance.UnReferenceCache(manifestName, true);  // 不走统一接口是因为manifest文件没有后缀
    
                if (m_manifest != null)
                {
                    string[] bundles = m_manifest.GetAllAssetBundles();
    
                    for (int i = 0; i < bundles.Length; ++i)
                    {
                        m_bundleNames.Add(bundles[i]);
                    }
                }
            }, false);
            bLoader.Load();
        }
        #endregion
    
        public void LoadPrefab(string strPath, string name, LoadedCallback onLoaded, bool async = true)
        {
            LoadAssetFromBundle(strPath, name, typeof(GameObject), onLoaded, async);
        }
    
        public void LoadMusic(string name, LoadedCallback onLoaded, bool async = true)
        {
            string path = string.Format("music/{0}", name);
            LoadAssetFromBundle(path, name, typeof(AudioClip), onLoaded, async);
        }
    
        public void LoadFont(string name, LoadedCallback onLoaded, bool async = true, bool inBundle = false)
        {
            string path = string.Format("ui/font/{0}", name);
            LoadAssetFromBundle(path, name, typeof(Font), onLoaded, async);
        }
    
        #region LoadScene
        public void LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded)
        {
            var activeSceneName = SceneManager.GetActiveScene().name;
            // 如果当前场景是要加载的场景 直接返回
            if (activeSceneName == name)
            {
                if (onLoaded != null)
                {
                    onLoaded(null);
                }
                return;
            }
    
            if (isAdditive)
            {
                RealLoadScene(name, scenePath, isAdditive, onLoaded);
            }
            else //大场景先加载idle过渡
            {
                RealLoadScene(name, scenePath, isAdditive, onLoaded);
            }
        }
    
        private void RealLoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded)
        {
            string abPath = "scenes/" + name;
            LoadAssetBundle(abPath, delegate (object data) {
                if (data == null)
                {
                    CallFunc_LoadedBack(onLoaded, null);
                    return;
                }
                __LoadScene(name, scenePath, isAdditive, onLoaded);  //场景的bundle在SceneLoader中自动卸载
            });
        }
    
        private void __LoadScene(string name, string scenePath, bool isAdditive, LoadedCallback onLoaded, bool async = true)
        {
            SceneLoader sLoader = LoaderPool.Instance.GetLoader<SceneLoader>();
            sLoader.Init(name, scenePath, isAdditive, onLoaded, async);
            StartLoad(sLoader, true);
        }
        #endregion
    
        //从Bundle中加载资源
        public void LoadAssetFromBundle(string path, string name, System.Type type, LoadedCallback onLoaded, bool async = true)
        {
            LoadAssetBundle(path, (data) => {
                AssetBundleCache abCache = data as AssetBundleCache;
                if (abCache == null)
                {
                    Debug.LogWarningFormat("LoadAssetFromBundle, load ab fail:{0}", path);
                    CallFunc_LoadedBack(onLoaded, null);
                    return;
                }
    
                // 开启任务去做加载
                BundleAssetLoader assetLoader = LoaderPool.Instance.GetLoader<BundleAssetLoader>();
                assetLoader.Init(abCache, name, type, onLoaded, async);
                StartLoad(assetLoader, async);
            }, async);
        }
    
        // 加载AssetBundle(先从persistentData读,没有找到则从streamingAssets读,带后缀)
        public void LoadAssetBundle(string path, LoadedCallback onLoaded, bool async = true)
        {
            string name = FileHelper.GenBundlePath(path);
            if (!HasAssetBundle(name))
            {
                if (onLoaded != null)
                {
                    onLoaded(null);
                }
                return;
            }
    
            // 加载依赖
            LoadDependencies(name, async);
    
            // 检查是否有缓存 有缓存说明依赖资源也是加载过了的
            AssetBundleCache abCache = ABCachePool.Instance.ReferenceCacheByName(name);
            if (abCache != null)
            {
                if (onLoaded != null)
                {
                    onLoaded(abCache);
                }
                return;
            }
    
            string fullpath = FileHelper.SearchFilePath(name);
            AssetBundleLoader bLoader = null;
            m_dicAllLoader.TryGetValue(name, out bLoader);
            if (bLoader == null)
            {
                string[] dependencies = m_manifest.GetDirectDependencies(name);
    
                bLoader = LoaderPool.Instance.GetLoader<AssetBundleLoader>();
                bLoader.Init(fullpath, name, dependencies, onLoaded, async);
                m_dicAllLoader.Add(name, bLoader);
                StartLoad(bLoader, async);
            }
            else
            {
                if (onLoaded != null)
                {
                    bLoader.AddLoadedCallback(onLoaded);
                }
                else
                {
                    bLoader.AddReferenced();
                }
            }
        }
    
        // 依赖
        // 加载依赖
        //asyncInFact 实际加载方式,如果依赖bundle是异步加载并且正在加载中,那么整个bundle的加载方式变成异步加载
        void LoadDependencies(string name, bool async)
        {
            if (m_manifest == null)
            {
                return;
            }
    
            string[] dependencies = m_manifest.GetDirectDependencies(name);
            for (int i = 0; i < dependencies.Length; ++i)
            {
                LoadAssetBundle(dependencies[i], null, async);
            }
        }
    
        void StartLoad(Loader loader, bool async, bool toHead = false)
        {
            // 异步加载或者加载还未准备好,则当做异步处理,外部控制是否加入队列开头
            // 同步加载,并且已经具备加载条件,则直接调用Load进行加载
            if (async || !loader.IsPrepareToLoad())
            {
                if (toHead)
                {
                    m_loaderQueue.Insert(0, loader);
                }
                else
                {
                    m_loaderQueue.Add(loader);
                }
            }
            else
            {
                m_loadings.Add(loader);
                loader.Load();
            }
        }
    
        public bool HasAssetBundle(string path)
        {
            path = path.Replace("/", "_").ToLower();
            return m_bundleNames.Count == 0 || m_bundleNames.Contains(path) || string.Equals(path, FileHelper.MANIFEST_NAME);
        }
    
        // 卸载关卡场景
        public void UnloadLevelScene(string sceneName, bool immediate)
        {
            SceneManager.UnloadSceneAsync(sceneName);
            UnloadSceneAssetBundle(sceneName, immediate);
        }
    
        // 卸载场景的AssetBundle
        public void UnloadSceneAssetBundle(string sceneName, bool immediate)
        {
            if (Config.Instance.UseAssetBundle)
            {
                string strABPath = "scenes/" + sceneName;
                UnloadAssetBundle(strABPath, immediate);
            }
        }
    
        // 卸载AssetBundle
        public void UnloadAssetBundle(string path, bool immediate = false)
        {
            string name = FileHelper.GenBundlePath(path);
            AssetBundleCache cache = ABCachePool.Instance.UnReferenceCache(name, immediate);
            if (cache != null)
            {
                UnloadDependencies(name, immediate);
            }
        }
    
        // 卸载依赖
        void UnloadDependencies(string name, bool immediate)
        {
            if (m_manifest == null)
            {
                return;
            }
    
            string[] dependencies = m_manifest.GetDirectDependencies(name);
            for (int i = 0; i < dependencies.Length; ++i)
            {
                UnloadAssetBundle(dependencies[i], immediate);
            }
        }
    
        private void CallFunc_LoadedBack(LoadedCallback callback, object data)
        {
            if (callback != null)
            {
                callback(data);
            }
        }
    }
  • 相关阅读:
    python处理csv数据
    python数据持久存储:pickle模块的基本使用
    使用SVD方法实现电影推荐系统
    使用矩阵分解(SVD)实现推荐系统
    多维数组分解----SVD在推荐系统中的应用-
    Logistic Regression--逻辑回归算法汇总**
    Netflix推荐系统:从评分预测到消费者法则
    从决策树学习谈到贝叶斯分类算法、EM、HMM
    数据挖掘中 决策树算法实现——Bash
    决策树算法
  • 原文地址:https://www.cnblogs.com/wang-jin-fu/p/11218659.html
Copyright © 2011-2022 走看看