天天看点

Unity AssetBundle加载

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Common.Event;
using UnityEngine;
using SandboxMap.Data;
using Object = UnityEngine.Object;

public enum AssetBundleType
{
    WoldMap,
    Construction,
    UI,
    NPC, //NPC模型的组件
    NPCModel,//若干AB组件结合成的NPC模型,3D人物模型
    FILMSCENE,//电影场景
}

/// <summary>
/// 资源加载工具
/// </summary>
public class AssetBundleTool : MonoManagerBase<AssetBundleTool>
{
    /// <summary>
    /// 预加载资源
    /// </summary>
    public void ReadyLoad()
    {
        ReadyLoadAsset = new List<string>();
        _elapsedTime = 0;
        StartCoroutine(ElapsedTimeEnumer());

        AddReadyLoadAsset("labelpanel", AssetBundleType.UI);
        AddReadyLoadAsset("commen_npc", AssetBundleType.NPC);
        AddReadyLoadAsset("commen_construction", AssetBundleType.Construction);

        AddReadyLoadAsset("StoryDialoguePanel", AssetBundleType.UI);
        AddReadyLoadAsset("TaskReceiveWindow", AssetBundleType.UI);
        AddReadyLoadAsset("TaskCommitWindow", AssetBundleType.UI);

        ReadyLoadBuild();
        
        Debug.Log("******AssetBundleReadyLoadAssetCount*******" + ReadyLoadAsset.Count);
    }

    /// <summary>
    /// 资源更新完成后 预加载
    /// </summary>
    public void PreLoad()
    {
        LoadResourceAsync<Font>("Font", AssetBundleType.UI, "DENG");
    }

    private float _elapsedTime;
    private List<string> ReadyLoadAsset;
    
    private void AddReadyLoadAsset(string data,AssetBundleType type)
    {
        data = data.ToLower();
        ReadyLoadAsset.Add(data);
        LoadResource(data, type, null);
    }

    public void ReadyLoadEnd(string data)
    {
        if (ReadyLoadAsset == null) return;
        if (ReadyLoadAsset.Contains(data))
        {
            ReadyLoadAsset.Remove(data);
        }
        else
        {
            Debug.Log("不在预加载集合中*********" + data);
        }

        if (ReadyLoadAsset.Count == 0)
        {
            ReadyLoadAsset = null;
            GameManager.Instance.ToStartGame();

            Debug.Log("******AssetBundleReadyLoadAssetEnd*******ElapsedTime:" + _elapsedTime);
        }
    }

    private IEnumerator ElapsedTimeEnumer()
    {
        while (ReadyLoadAsset != null)
        {
            _elapsedTime += Time.deltaTime;
            yield return 1;
        }
    }
    
    /// <summary>
    /// 预加载建筑
    /// </summary>
    /// <param name="data"></param>
    private void ReadyLoadBuild()
    {
        
        BuildingConfigData data = ConfigManager.Instance.load<global::BuildingConfigData>();
        if (data == null || data.datas == null) return;
        for (int i = 0; i < data.datas.Length; i++)
        {
            ConstructionConfig building = data.datas[i];
            AddReadyLoadAsset(building.modeID + "_" + building.level , AssetBundleType.Construction);
        }
        
    }

    /*
    /// <summary>
    /// 同步获得资源
    /// </summary>
    /// <param name="data"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public GameObject GetReadyObject(string data, AssetBundleType type)
    {
        string resUrl = ClientInfo.DataPath + "/AssetBundleDatas/" + type
                        + "/" + data + ".data";
        try
        {
            return AssetTool.GetAssetByName<GameObject>(ResourcesDic[resUrl],data) as GameObject;
        }
        catch (Exception e)
        {
            throw new Exception(data + ":没有做预加载" + e.Message);
        }
    }
    */

    private EventDispatcher _eventManager;

    public EventDispatcher EventManager => _eventManager;

    private Queue<Dependence> _dependences;
    
    /// <summary>
    /// 依赖资源
    /// </summary>
    private List<string> DependencesList;

    /// <summary>
    /// 资源缓存字典
    /// </summary>
    public Dictionary<string, UnityEngine.Object[]> AssetsDic;
    

    public bool CheckAssetsContains(string url)
    {
        if (AssetsDic!=null && AssetsDic.ContainsKey(url))
        {
            return true;
        }

        return false;
    }

    
    public void AddAssets2Dic(string key,UnityEngine.Object[] assets)
    {
        if (AssetsDic == null)
        {
            AssetsDic=new Dictionary<string, UnityEngine.Object[]>();
        }
        
        if (AssetsDic.ContainsKey(key))
        {
            Debug.LogError("已存在ResourcesDic*********" + key + "*********" + assets);
        }
        else
        {
            AssetsDic.Add(key, assets);
        }
    }

    public Object[] GetAssets(string key)
    {
        if (AssetsDic != null && AssetsDic.ContainsKey(key))
        {
            return AssetsDic[key];
        }

        return null;
    }


    /// <summary>
    /// 正在加载中
    /// </summary>
    public List<string> InLoadResources;

    private AssetBundleManifest _woldMapManifest; //
    private AssetBundleManifest _constructionManifest;
    private AssetBundleManifest _uiManifest;
    private AssetBundleManifest _npcManifest;

    private string PATHRESROOTDIR;


    public void unload(string assetBundleName, AssetBundleType type = AssetBundleType.UI)
    {
        assetBundleName = assetBundleName.ToLower();

        string resUrl = ClientInfo.DataPath + "/AssetBundleDatas/" + type + "/" + assetBundleName + ".data";

        UnityEngine.Object[] ress = GetAssets(resUrl);
        AssetsDic.Remove(resUrl);
        
        Resources.UnloadUnusedAssets();
        GC.Collect();
    }



    /// <summary>
    /// 加载item方便实用
    /// </summary>
    /// <param name="assetBundleName"></param>
    /// <param name="type"></param>
    /// <param name="assetName"></param>
    /// <returns></returns>
    public RectTransform Load(string assetBundleName, string assetName = "")
    {
        GameObject asset = LoadResource<GameObject>(assetBundleName, AssetBundleType.UI, assetName);
        return Instantiate(asset).transform as RectTransform;
    }

    /// <summary>
    /// 同步加载
    /// </summary>
    /// <param name="assetBundleName"></param>
    /// <param name="type"></param>
    /// <param name="back"></param>
    /// <param name="assetName"></param>
    /// <returns></returns>
    public T LoadResource<T>(string assetBundleName, AssetBundleType type, string assetName = "")
        where T : UnityEngine.Object
    {
        assetBundleName = assetBundleName.ToLower();
        assetName = assetName.ToLower();

        string resUrl = ClientInfo.DataPath + "/AssetBundleDatas/" + type + "/" + assetBundleName + ".data";
        if (string.IsNullOrEmpty(assetName))
            assetName = assetBundleName;
        
        if (CheckAssetsContains(resUrl))
        {
            return AssetTool.GetAsset<T>(GetAssets(resUrl), assetName) as T;
        }
        else
        {
            AssetBundle ab = null;
            ab = AssetBundle.LoadFromFile(resUrl);

            if (ab == null)
            {
                Debug.LogError("文件不存在! fileName : " + resUrl);
                return null;
            }

            

            UnityEngine.Object[] assets = ab.LoadAllAssets();
            AddAssets2Dic(resUrl, assets);
            
            T obj = AssetTool.GetAsset<T>(assets,assetName) as T;

            ab.Unload(false);
            return obj;
        }
    }


    public void LoadSceneAsync(string assetBundleName, AssetBundleType type, Action action)
    {
        StartCoroutine(LoadSceneAsyncFun(assetBundleName, type, action));
    }

    private IEnumerator LoadSceneAsyncFun(string assetBundleName, AssetBundleType type, Action action)
    {
        assetBundleName = assetBundleName.ToLower();

        string resUrl = ClientInfo.DataPath + "/AssetBundleDatas/" + type + "/" + assetBundleName + ".data";

        if (CheckAssetsContains(resUrl) && action != null)
        {
            action();
        }
        else
        {
            AssetBundleCreateRequest abReq;
            AssetBundle ab = null;
            abReq = AssetBundle.LoadFromFileAsync(resUrl);
            yield return abReq;
            ab = abReq.assetBundle;

            if (ab == null)
            {
                Debug.LogError("文件不存在! fileName : " + resUrl);
                if (action != null) action();
            }

            if (ab.isStreamedSceneAssetBundle)
            {
                AddAssets2Dic(resUrl, new Object[] { ab });
                if (action != null) action();
                yield break;
            }

            if (action != null) action();
        }
    }




    /// <summary>
    /// 资源异步加载,返回Asset资源
    /// </summary>
    /// <param name="assetBundleName">包名</param>
    /// <param name="type">资源类型</param>
    /// <param name="assetName">在包中的资源名</param>
    /// <param name="action">加载完成后的回调函数</param>
    /// <param name="save">是否需要在内存中保留assetBundle镜像资源</param>
    /// <typeparam name="T"></typeparam>
    public void LoadResourceAsync<T>(string assetBundleName, AssetBundleType type, string assetName = "", 
        Action<T> action = null,bool save = true) where T : UnityEngine.Object
    {
        StartCoroutine(LoadResourceAsyncFun(assetBundleName, type, assetName, action, save));
    }

    private IEnumerator LoadResourceAsyncFun<T>(string assetBundleName, AssetBundleType type, string assetName, 
        Action<T> action,bool save) where T : UnityEngine.Object
    {
        assetBundleName = assetBundleName.ToLower();
        assetName = assetName.ToLower();

        string resUrl = ClientInfo.DataPath + "/AssetBundleDatas/" + type + "/" + assetBundleName + ".data";
        string key = resUrl + assetName;

        if (CheckAssetsContains(key) && action != null)
        {
            Object obj = AssetTool.GetAsset<T>(GetAssets(resUrl), assetBundleName);
            
            if (obj is T)
            {
                action(obj as T);
            }
            else if (obj is AssetBundle)
            {
                action(null);
            }
        }
        else
        {
            AssetBundleCreateRequest abReq;
            AssetBundle ab = null;
            abReq = AssetBundle.LoadFromFileAsync(resUrl);
            yield return abReq;
            ab = abReq.assetBundle;

            if (ab == null)
            {
                Debug.LogError("文件不存在! fileName : " + resUrl);
                if(action != null) action(null);
            }
        
            if (string.IsNullOrEmpty(assetName))
                assetName = assetBundleName;

            if(ab.isStreamedSceneAssetBundle)
            {
                AddAssets2Dic(resUrl, new Object[] { ab });
                if (action != null) action(null);
                yield break;
            }

            AssetBundleRequest assets = ab.LoadAllAssetsAsync();
            yield return assets;
            AddAssets2Dic(resUrl,assets.allAssets);
            T obj = AssetTool.GetAsset<T>(assets.allAssets,assetName) as T;

            if (!save)
            {
                ab.Unload(false);    
            }
            
            if (action != null) action(obj);
        }
    }


    /// <summary>
    /// 根据name加载一个预设文件,设置为parent的子物体  -- 异步 --
    /// </summary>
    public void LoadResource(string data, AssetBundleType type,  Action<GameObject> back)
    {
        data = data.ToLower();
       
        if (_eventManager == null)
        {
            _eventManager = new EventDispatcher();
            _eventManager.AddEventListener(AssetBundleEventType.LoadEnd, LoadEvent);
            _dependences = new Queue<Dependence>();
            DependencesList = new List<string>();
            InLoadResources = new List<string>();
        }
        
        StartCoroutine(Load(data, type, back));
    }
    
    private IEnumerator Load(string data, AssetBundleType type, Action<GameObject> back)
    {
        string resUrl = ClientInfo.DataPath + "/AssetBundleDatas/" + type + "/" + data + ".data";

        while (InLoadResources.Contains(resUrl))
        {
            yield return 1;
        }
        
        if (CheckAssetsContains(resUrl))
        {
            _eventManager.DispatchEvent(new GameEventArgs(AssetBundleEventType.LoadEnd));
            GameObject obj = AssetTool.GetAsset<GameObject>(GetAssets(resUrl), data) as GameObject;
            back?.Invoke(obj);
            yield break;
        }

        if (!File.Exists(resUrl))
        {
            Debug.LogWarning("文件不存在: " + resUrl);
            ReadyLoadEnd(data);
            _eventManager.DispatchEvent(new GameEventArgs(AssetBundleEventType.LoadEnd));
            back?.Invoke(null);
            yield break;
        }

        InLoadResources.Add(resUrl);
        
        AssetBundleManifest manifest = null;
        switch (type)
        {
                case AssetBundleType.WoldMap:
                    if (_woldMapManifest != null) manifest = _woldMapManifest;
                    break;
                case AssetBundleType.Construction:
                    if (_constructionManifest != null) manifest = _constructionManifest;
                    break;
                case AssetBundleType.UI:
                    if (_uiManifest != null) manifest = _uiManifest;
                    break;
                case AssetBundleType.NPC:
                    if (_npcManifest != null) manifest = _npcManifest;
                    break;
        }
        
        if (manifest == null)
        {
            string manifestPath =
                Path.Combine(ClientInfo.DataPath + "/AssetBundleDatas/", type.ToString());
            
            manifestPath = Path.Combine(manifestPath, type.ToString());
            
//            if (!File.Exists(manifestPath))
//            {
//                _eventManager.DispatchEvent(new GameEventArgs(AssetBundleEventType.LoadEnd));
//                back?.Invoke(null);
//            }
            
            AssetBundle asset = AssetBundle.LoadFromFile(manifestPath);

            manifest = asset.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            
            switch (type)
            {
                case AssetBundleType.WoldMap:
                    _woldMapManifest = manifest;
                    break;
                case AssetBundleType.Construction:
                    _constructionManifest = manifest;
                    break;
                case AssetBundleType.UI:
                    _uiManifest = manifest;
                    break;
                case AssetBundleType.NPC:
                    _npcManifest = manifest;
                    break;
            }
            
            asset.Unload(false);
        }
        Dependence dep = new Dependence(data, type.ToString(), manifest, back);
        _dependences.Enqueue(dep);
        if (_isFrist)
        { 
            _isFrist = false;
            _eventManager.DispatchEvent(new GameEventArgs(AssetBundleEventType.LoadEnd));
        }
    }
    
    private bool _isFrist = true;
    
    private void LoadEvent(GameEventArgs e)
    {
        if (_dependences.Count > 0)
        {
            _dependences.Dequeue().Init();
        }
        else
        {
            _isFrist = true;

        }
    }

    public bool GetDepContain(string key)
    {
        if (!DependencesList.Contains(key))
        {
            DependencesList.Add(key);
            return false;
        }

        return true;
    }
    
    
    
    public void LoadScene(string sceneName, Action action)
    {
        StartCoroutine(LoadSceneFun(sceneName, action));
    }

    private IEnumerator LoadSceneFun(string sceneName, Action action)
    {
        sceneName = sceneName.ToLower();

        string resUrl = ClientInfo.DataPath + "/AssetBundleDatas/WorldMap/" + sceneName + ".data";
        string key = resUrl;

        if (CheckAssetsContains(key) && action != null)
        {
            action();
        }
        else
        {
            AssetBundleCreateRequest abReq;
            AssetBundle ab = null;
            abReq = AssetBundle.LoadFromFileAsync(resUrl);
            yield return abReq;
            /*
            ab = abReq.assetBundle;

            if (ab == null)
            {
                Debug.LogError("文件不存在! fileName : " + resUrl);
                if(action != null) action();
            }

            AssetBundleRequest assets = SceneManager.LoadScene()
            yield return assets;
            AddAssets2Dic(resUrl,assets.allAssets);

            ab.Unload(false);    
            */
            if (action != null) action();
        }
    }
}

public static class AssetBundleEventType
{
    public const string LoadEnd = "LOADEND";
}

public class Dependence
{
    private readonly string _data;
    private readonly string _url;
    private readonly string _type;
    private readonly AssetBundleManifest _manifest;
    private readonly Action<GameObject> _back;

    public Dependence(string data, string type, AssetBundleManifest manifest, Action<GameObject> back)
    {
        _data = data;
        _url = ClientInfo.DataPath + "/AssetBundleDatas/" + type + "/" + data + ".data";
        _type = type;
        _manifest = manifest;
        _back = back;
    }

    public void Init()
    {
        AssetBundleTool.instance.StartCoroutine(LoadDependences());
    }
    
    /// <summary>
    /// 加载依赖项
    /// </summary>
    /// <returns></returns>
    private IEnumerator LoadDependences()
    {
        AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(_url);

        yield return request;

        if (string.Equals(_data, "commen_npc") || string.Equals(_data, "commen_construction"))
        {
            AssetBundleTool.instance.ReadyLoadEnd(_data);
            AssetBundleTool.instance.EventManager.DispatchEvent(new GameEventArgs(AssetBundleEventType.LoadEnd));
            yield break;
        }

        AssetBundle assetBundle = request.assetBundle;

        if (assetBundle == null)
        {
            Debug.LogError("SceneProp assetsBundle is null! fileName : " + _url);
            yield break;
        }

        
        AssetBundleRequest result = assetBundle.LoadAllAssetsAsync();

        yield return result;

        object temp = AssetTool.GetAsset<GameObject>(result.allAssets,_data);

        UnityEngine.Object obj = temp as UnityEngine.Object;
        _back?.Invoke(obj as GameObject);

        AssetBundleTool.instance.ReadyLoadEnd(_data);
        AssetBundleTool.instance.AddAssets2Dic(_url, result.allAssets);
        AssetBundleTool.instance.InLoadResources.Remove(_url);
        
        AssetBundleTool.instance.EventManager.DispatchEvent(new GameEventArgs(AssetBundleEventType.LoadEnd));


        if(_type == "Construction" || _type == "NPC")
        {
            assetBundle.Unload(false);
        }
    }


}


public class AssetTool
{
    public static UnityEngine.Object GetAsset<T>(UnityEngine.Object[] assets, string name)
    {
        UnityEngine.Object temp = null;
        for (int i = 0; i < assets.Length; i++)
        {
            temp = assets[i];
            if (temp is T)
            {
                string nameValue = temp.ToString();
                string tempName = nameValue.Substring(0, nameValue.IndexOf(" ")).ToLower();
                if (tempName == name)
                {
                    //Debug.Log(tempName);
                    return temp;
                }
            }
        }
        
        return null;
    }

    public static T[] GetAssetAll<T>(UnityEngine.Object[] assets)
    {
        List<T> tempResult= new List<T>();
        System.Object temp = null;
        for (int i = 0; i < assets.Length; i++)
        {
            temp = assets[i];
            if (temp is T)
            {
                //Debug.Log(temp);
                tempResult.Add((T)temp);
            }
        }

        return tempResult.ToArray();
    }
}