unity遊戲框架學習-資源加載模塊

概述:http://www.javashuo.com/article/p-nggymcxb-bw.htmlhtml

本篇咱們實現unity裏的加載模塊,他的主要功能是,業務傳入資源名字和資源類型,加載模塊加載到對應的資源後返回給業務,業務不須要關心該資源是從本地加載仍是從AssetBundle里加載。緩存

加載模塊分兩部分1.各資源的加載器,例如ab包加載器、Asset加載器、網絡下載。2.各加載器的管理類,提供給業務的接口都在這裏服務器

 

須要支持的能力網絡

1.能切換不一樣加載模式 開發階段編輯器運行直接加載資源無需打ab包,測試或正式發佈階段經過ab包加載資源併發

2.緩存機制 能定時清理長時間未使用的資源內存框架

3.既有同步加載 也有異步加載異步

 

複雜點:async

1.根據業務傳入的資源名字,獲取到editor路徑、ab包名字。須要事先根據資源名字保存資源的路徑、ab包路徑配置。編輯器

2.ab包的引用計數維護:加載時ReferencedCount+1,卸載時ReferencedCount-1。ide

兩種引用: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)。須要業務手動的釋放調用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包。

 

1、加載器實現

上篇咱們有說到,unity有四種加載方式

1.AssetDatabase:在編輯器內加載卸載資源,並不能在遊戲發佈時使用,它只能在編輯器內使用。可是,它加載速度快,使用簡單。

2.Resources:由於使用Resources文件夾沒法熱更,本片篇就不實現此途徑了。

3.AssetBundle:參考http://www.javashuo.com/article/p-kxvvqcjh-e.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);
        }
    }
}

2、緩存池,緩存加載器

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

3、加載管理器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);
        }
    }
}
相關文章
相關標籤/搜索