Unity简单资源加载框架(八):使用AssetLoader类加载使用AssetLoader加载非prefab资源与自动卸载策略

1.AssetLoader

/****************************************************
    文件:AssetLoader.cs
	作者:赵深圳
    邮箱: 1329346609@qq.com
    日期:2022/5/9 10:39:33
	功能:资源加载器
*****************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;

/// <summary>
/// 资源加载器
/// </summary>
public class AssetLoader : Singleton<AssetLoader>
{
    /// <summary>
    /// 平台对应的只读路径下的资源
    /// key 模块名字
    /// value 模块下所有的资源
    /// </summary>
    public Dictionary<string, Hashtable> base2Assets;


    public AssetLoader()
    {
        base2Assets = new Dictionary<string, Hashtable>();
    }

    /// <summary>
    /// 加载模块对应的全局AssetBundle资源管理文件
    /// </summary>
    /// <param name="moduleConfig">模块名字</param>
    /// <param name="onCompelte">加载完成后的回调</param>
    public async Task<ModuleABConfig> LoadAssetBundleConfig(string moduleName)
    {
#if UNITY_EDITOR
        if (GlobalConfig.BundleMode == false)
        {
            return null;
        }
        else
        {
            return await LoadAssetBundleConfig_Runtime(moduleName);
        }
#else
         return await LoadAssetBundleConfig_Runtime(moduleName);
#endif
    }

    /// <summary>
    /// 加载本地的模块的AB配置文件
    /// </summary>
    /// <param name="moudleName">模块的名字</param>
    /// <returns></returns>
    public async Task<ModuleABConfig> LoadAssetBundleConfig_Runtime(string moudleName)
    {
        string url = Application.streamingAssetsPath + "/" + moudleName + "/" + moudleName.ToLower() + ".json";

        UnityWebRequest request = UnityWebRequest.Get(url);

        await request.SendWebRequest();

        if (string.IsNullOrEmpty(request.error))
        {
            return LitJson.JsonMapper.ToObject<ModuleABConfig>(request.downloadHandler.text);
        }
        return null;
    }

    /// <summary>
    /// 根据模块的json文件创角内存中资源管理器
    /// </summary>
    /// <param name="moduleABConfig"></param>
    /// <returns></returns>
    public Hashtable ConfigAssembly(ModuleABConfig moduleABConfig) 
    {
        //根据ab配置文件,得到所有的bundleinfo,并转换成bundlRef
        Dictionary<string, BundleRef> name2BundleRef = new Dictionary<string, BundleRef>();
        foreach (KeyValuePair<string,BundleInfo> keyValue in moduleABConfig.BundleInfoDic) 
        {
            string bundleName = keyValue.Key;
            BundleInfo bundleInfo = keyValue.Value;

            name2BundleRef[bundleName] = new BundleRef(bundleInfo);
        }

        Hashtable path2AssetRef = new Hashtable();
        //获取所有Asset资源信息
        for (int i = 0; i < moduleABConfig.AssetInfoArr.Length; i++)
        {
            AssetInfo assetInfo = moduleABConfig.AssetInfoArr[i];
            AssetRef assetRef = new AssetRef(assetInfo);
            assetRef.bundleRef = name2BundleRef[assetInfo.bundle_name];
            int count = assetInfo.dependenciesLst.Count;
            assetRef.dependencies = new BundleRef[count];

            for (int index = 0; index < count; index++)
            {
                string bundleName = assetInfo.dependenciesLst[index];
                assetRef.dependencies[index] = name2BundleRef[bundleName];
            }
            path2AssetRef.Add(assetInfo.asset_path,assetRef);
        }
        return path2AssetRef;
    }

    /// <summary>
    /// 加载游戏对象
    /// </summary>
    /// <param name="moduleName">模块名字</param>
    /// <param name="path">游戏对象的相对路径</param>
    /// <returns></returns>
    public GameObject Clone(string moduleName,string path)
    {
        //获取当前路径资源所在内存中信息
        AssetRef assetRef= LoadAssetRef<GameObject>(moduleName,path);

        if (assetRef==null||assetRef.asset==null)
        {
            return null;
        }
        //实例化资源
        GameObject gameObject = UnityEngine.Object.Instantiate(assetRef.asset) as GameObject;
        if (assetRef.children==null)
        {
            assetRef.children = new List<GameObject>();
        }
        //把当前实例化资源加入到加入到Asset的被依赖列表中
        assetRef.children.Add(gameObject);
        return gameObject;
    }

    /// <summary>
    /// 加载AssetRef对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="moduleName">模块名字</param>
    /// <param name="assetPath">资源的相对路径</param>
    /// <returns></returns>
    private AssetRef LoadAssetRef<T>(string moduleName, string assetPath) where T : UnityEngine.Object 
    {
#if UNITY_EDITOR
        if (GlobalConfig.BundleMode == false)
        {
            return LoadAssetRef_Editor<T>(moduleName, assetPath);
        }
        else 
        {
            return LoadAssetRef_Runtime<T>(moduleName,assetPath);
        }
#else
            return LoadAssetRef_Runtime<T>(moduleName,assetPath);
#endif
    }

    /// <summary>
    /// 编译器模式下加载AssetRef对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="moduleName">模块名字</param>
    /// <param name="assetPath">资源的相对路径</param>
    /// <returns></returns>
    private AssetRef LoadAssetRef_Editor<T>(string moduleName, string assetPath) where T : UnityEngine.Object 
    {
#if UNITY_EDITOR
        if (string.IsNullOrEmpty(assetPath))
        {
            return null;
        }
        AssetRef assetRef = new AssetRef(null);
        assetRef.asset = AssetDatabase.LoadAssetAtPath<T>(assetPath);
        return assetRef;
#else
         return null;
#endif
    }

    /// <summary>
    /// AB包模式下加载的AssetRef对象
    /// </summary>
    /// <typeparam name="T">要加载的资源类型</typeparam>
    /// <param name="moduleName">模块名字</param>
    /// <param name="assetPath">资源的相对路径</param>
    /// <returns></returns>
    private AssetRef LoadAssetRef_Runtime<T>(string moduleName, string assetPath) where T : UnityEngine.Object 
    {
        if (string.IsNullOrEmpty(assetPath))
        {
            return null;
        }

        Hashtable module2AssetRef;
        //得到这个模块的所有资源集合
        bool moduleExsit = base2Assets.TryGetValue(moduleName,out module2AssetRef);
        if (moduleExsit==false)
        {
            Debug.LogError("未找到资源对应模块:moduleName="+moduleName+"assetPath="+assetPath);
            return null;
        }
        //得到资源中内存对象
        AssetRef assetRef = (AssetRef)module2AssetRef[assetPath];
        if (assetPath==null)
        {
            Debug.LogError("未找到资源:moduleName=" + moduleName + "assetPath=" + assetPath);
            return null;
        }
        //如果已经加载直接返回
        if (assetRef.asset!=null)
        {
            return assetRef;
        }

        //处理AssetRef依赖的BundleRef列表
        foreach (BundleRef oneBundleRef in assetRef.dependencies)
        {
            if (oneBundleRef.bundle==null)//判断当前依赖项的bundle有没有被加载
            {
                string bundlePath = BundlePath(moduleName,oneBundleRef.bundleInfo.bundle_name);
                //加载当前依赖项的bundle
                oneBundleRef.bundle = AssetBundle.LoadFromFile(bundlePath);
            }
            if (oneBundleRef.chilren==null)
            {
                oneBundleRef.chilren = new List<AssetRef>();
            }
            //设置依赖项的被依赖资源列表,把自己加入到依赖项的被依赖的资源列表中
            oneBundleRef.chilren.Add(assetRef);
        }

        //处理assetRef属于哪个BundleRef对象
        //判断自己所属的Bundle有没有被加载
        BundleRef bundleRef = assetRef.bundleRef;
        if (bundleRef.bundle==null)
        {
            //加载自身所在的AB包
            bundleRef.bundle = AssetBundle.LoadFromFile(BundlePath(moduleName,bundleRef.bundleInfo.bundle_name));
        }

        //把自己加到自己所属的Bundle的被依赖资源列表中
        if (bundleRef.chilren==null)
        {
            bundleRef.chilren = new List<AssetRef>();
        }

        bundleRef.chilren.Add(assetRef);

        //从bundle中提取asset

        assetRef.asset = assetRef.bundleRef.bundle.LoadAsset<T>(assetRef.assetInfo.asset_path);

        if (typeof(T) == typeof(GameObject) && assetRef.assetInfo.asset_path.EndsWith(".prefab"))
        {
            assetRef.isGameObject = true;
        }
        else {
            assetRef.isGameObject = false;            
        }
        return assetRef;
    }


    /// <summary>
    /// 创建资源对象
    /// </summary>
    /// <typeparam name="T">资源类型</typeparam>
    /// <param name="moduleName">模块名字</param>
    /// <param name="assetPath">资源路径</param>
    /// <param name="gameObject">资源加载后,所挂载到的游戏对象</param>
    /// <returns></returns>
    public T CreateAsset<T>(string moduleName, string assetPath, GameObject gameObject) where T : UnityEngine.Object
    {
        if (typeof(T)==typeof(GameObject)||(!string.IsNullOrEmpty(assetPath)&&assetPath.EndsWith(".prefab")))
        {
            Debug.LogError("不可以加载GameObject类型,请直接使用AssetLoader.Instance.Clone接口,path:"+assetPath);
            return null;
        }
        if (gameObject==null)
        {
            Debug.LogError("CreateAsset必须传递一个gameObject其将要被挂载的GameObject对象!");
            return null;
        }

        AssetRef assetRef = LoadAssetRef<T>(moduleName,assetPath);
        if (assetRef==null||assetRef.asset==null)
        {
            return null;
        }

        if (assetRef.children==null)
        {
            assetRef.children = new List<GameObject>();
        }
        assetRef.children.Add(gameObject);
        return assetRef.asset as T;
    }


    public void Unload(Dictionary<string,Hashtable> module2Assets) 
    {
        foreach (string  moduleName in module2Assets.Keys)
        {
            Hashtable path2AssetRef = module2Assets[moduleName];
            if (path2AssetRef==null)
            {
                continue;
            }
            foreach (AssetRef assetRef in path2AssetRef.Values)
            {
                if (assetRef.children==null||assetRef.children.Count==0)
                {
                    continue;
                }

                for (int i = assetRef.children.Count - 1; i >= 0; i--)
                {
                    GameObject go = assetRef.children[i];
                    if (go==null)
                    {
                        assetRef.children.RemoveAt(i);
                    }
                }

                //如果当前资源没有被任何GameObject所依赖,那么就可以卸载了
                if (assetRef.children.Count==0)
                {
                    assetRef.asset = null;
                    Resources.UnloadUnusedAssets();
                    //对于assetRef所属的bundle 解除关系
                    assetRef.bundleRef.chilren.Remove(assetRef);
                    if (assetRef.bundleRef.chilren.Count==0)
                    {
                        assetRef.bundleRef.bundle.Unload(true);
                    }
                    //对于assetRef所依赖的那些bundle列表,解除关系

                    foreach (BundleRef bundleRef in assetRef.dependencies)
                    {
                        bundleRef.chilren.Remove(assetRef);
                        if (bundleRef.chilren.Count==0)
                        {
                            bundleRef.bundle.Unload(true);
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// 工具函数,根据模块名字和bundle名字,返回其实际资源路径
    /// </summary>
    /// <param name="moduleName">模块名字</param>
    /// <param name="bundleName">bundle名字</param>
    /// <returns></returns>
    private string BundlePath(string moduleName,string bundleName) 
    {
        return Application.streamingAssetsPath + "/" + moduleName + "/" + bundleName;
    }
}

2.GameEntry

/****************************************************
    文件:GameEntry.cs
	作者:赵深圳
    邮箱: 1329346609@qq.com
    日期:2022/5/9 10:43:14
	功能:游戏启动入口
*****************************************************/

using UnityEngine;

public class GameEntry : MonoBehaviour 
{
    public static GameEntry Instance;

    private async void Awake()
    {
        InitGlobal();

        //启动模块
        ModuleConfig launchModule = new ModuleConfig
        {
            moduleName="launch",
            moduleVersion="202205091051",
            moduleUrl="127.0.0.1"
        };

        bool result= await ModuleManager.Instance.Load(launchModule);

        if (result)
        {
            Log.ZSZLog(LogCategory.Resource,"Lua 代码开始");
        }
    }

    private void InitGlobal() 
    {
        Instance = this;
        GlobalConfig.HotUpdate = false;
        GlobalConfig.BundleMode = false;
        DontDestroyOnLoad(this.gameObject);
    }

    private void Update()
    {
        AssetLoader.Instance.Unload(AssetLoader.Instance.base2Assets);
    }
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值