Unity AssetBundle 分资源包管理和规范个人总结

时间:2024-04-14 22:05:03

项目急急封测。才发现之前偷懒资源预制都丢在Resources中导致后面修改一个预制热更包就花费了几百M。。

发现问题。就要解决问题。过去欠的债。总是要还回来。

 

由此对项目目录和资源进行一边系统梳理

 

项目目录情况 

-----文档目录-----
【01.Art】该目录用于存放各类模型,特效,材质,贴图等美术资源,主要由美术进行整理及分类,程序只在必要时进行调整
【02.Scenes】该目录用于存放所有场景文件,统一管理,方便快速寻找并打开场景。
【03.UI】该目录用于存放与游戏界面相关的资源文件,比如Logo,各类图标,按钮,弹窗等等Sprite资源。
【04.Prefabs】该目录用于存放游戏中需要用到的预制体
【05.Audios】该目录用于存放各类音效资源
【06.Input】该目录用于存放与玩家输入相关的资源文件,比如PC输入,触屏输入,游戏手柄输入,自定义输入等。
【07.Network】该目录用于存放与网络通讯相关的资源文件,比如服务器连接,网络缓存,即时通讯等。
【08.Database】该目录用于存放与数据库操作相关的资源文件,比如本地存储,网络存储,模板表配置等。
【09.Steam】该目录用于存放 steamAPI相关文件
【10.Scripts】该目录用于存放项目中的各类脚本文件
【11.Others】该目录用于存放存放暂时不知道如何归类的资源文件
【12.Textures】该目录用于存放 动态纹理贴图 RenderTexture
【22.Client】该目录用于存放 战斗编辑器相关文件
【Editor】:该目录下的代码可调用Unity Editor 的API,存放扩展编辑器的代码。编译时不会被打包到游戏包中。其中testunit目录用来存放单元测试脚步。仅在编辑器模式下生效。不会打包入发布版本
【Plugins】:插件目录,该目录在编译项目时,会优先编译,方便项目中代码调用。
【Resources】:项目中默认的资源路径,会直接打包到游戏包中,所以尽量精简存放及使用
【XXXTest】:程序开发和调试使用的临时目录。便于各自程序自行管理。开发调试完毕的代码,资源,预制。应该按照统一的目录规范和命名。归纳到上述工作目录中。

-----子级目录-----
各个文档目录的子级目录依照各自的需求进行划分,所有资源文件不允许直接存放在文档目录的根目录下,应至少分类存储在二级目录中。
所有需要打进AB包的资源都需要进行管理,应统一存放在有【_AB】后缀二级目录下(比如Image_AB,UIPrefabs_AB等),之后可以根据对应的功能再划分三级目录进行分类存储
ps:
不需要动态加载的资源:【03.UI】/【Image】
需要动态加载的资源:【03.UI】/【Image_AB】
这两个目录下

-----命名规范-----
Assets目录中的所有资源文件名(场景、脚本、预制、模型、特效,材质,贴图)均采用【大驼峰式命名法】,即每一个单词的首字母都大写。
各资源在命名时尽量加上各自分类目录的【前缀】(比如Sence_,UI_,Prefab_)
命名的【主体】应使用能够描述其功能或意义的英文单词或词组,比如【(UI_)HeroInfo_Tab_英雄切换】代表HeroInfo预制中用于英雄切换的Tab
资源文件属于同一类型,需要添加编号加以区别的情况下,采用“_XX”的【后缀】进行分类处理,比如Sence_01,Sence_02。
ps:
仅美术资源允许使用中文以提高辨识度(不包括场景命名)
脚本命名必须严格遵循命名规范

 

 

使用官方插件AssetBundle Browser进行资源管理和复查

 

根据工具找到相关依赖关系和重复auto打入包内的资源进行后期持续性优化。

理论预期是能达到分包精细。各功能独立分包。公用的依赖资源也有打包而没有被重复自动引用造成冗余。

实际使用过程中应该按照untiy的AssetBundle流程。依照依赖关系的树形结构从叶子节点依序加载。最后在实例化资源对象。以防止资源丢失白块等问题。

 

代码层面使用了AssetManager进行资源manifest的管理和加载

public class AssetManager
{
    /// <summary>
    /// 单例对象
    /// </summary>
    private static AssetManager _instance = new AssetManager();


    //缓存已加载的AssetBundle
    public Dictionary<string, AssetBundle> AssetBundleDic = new Dictionary<string, AssetBundle>();

    //缓存所有Manifest依赖关系
    private static AssetBundleManifest AllManifest;

    public static AssetManager Instance
    {
        get
        {
            if (_instance != null)
                return _instance;
            else
            {
                _instance = new AssetManager();
                //单例初始化 manifest依赖关系
                var bundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "StandaloneWindows"));
                AllManifest = bundle.LoadAsset("AssetBundleManifest") as AssetBundleManifest;
                if (AllManifest == null)
                    Debug.LogError("mainfest 异常!!?");
                return _instance;
            }
            
        }
    }


    
    public static AssetBundle GetBundle(string bundleName)
    {
        
        if (Instance.AssetBundleDic.ContainsKey(bundleName))
        {
            Debug.Log("<color=blue>获取已加载的bundle " + bundleName + "</color>");
            return Instance.AssetBundleDic[bundleName] as AssetBundle;
        }
        else
        {
            Debug.Log("新加载 加载bundle " + bundleName);
            string path = Path.Combine(Application.streamingAssetsPath, bundleName);
            Debug.Log("GetBundle " + path);
            LoadDepend(bundleName);//引用先加载
            AssetBundle ab = AssetBundle.LoadFromFile(path);
            Instance.AssetBundleDic.Add(bundleName, ab);
            
            return ab;
        }
      
    }

    private static void LoadDepend(string bundleName)
    {
        string[] bundleList = AllManifest.GetAllDependencies(bundleName);
        foreach (string name in bundleList)
        {
            Debug.Log("间接引用 加载bundle " + name);
            GetBundle(name);
        }
    }

    public static T Get<T>(string buddle, string name) where T : UnityEngine.Object
    {
        
        Debug.Log("getbundle " + buddle + " " + name);

        try
        {
#if UNITY_EDITOR
            var texPath = AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(buddle, name)[0];

            T obj = AssetDatabase.LoadAssetAtPath<T>(texPath);

            return obj;
#else
	                AssetBundle ab = GetBundle(buddle);
	                var unityAsset = ab.LoadAsset<T>(name);
	                return unityAsset;
#endif
        }
        catch (System.Exception ex)
        {
            Debug.LogError("getbundle " + buddle + " " + name +" not exist" );
            return null;
        }
    }

    public static T[] GetAllsub<T>(string buddle, string name) where T : UnityEngine.Object
    {
        //Debug.Log("loadAll " + buddle +" " + name);
#if UNITY_EDITOR

        //string[] pathList = AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(buddle, name);
        //for (int i = 0; i < pathList.Length; i++)
        //{
        //    Debug.Log(pathList[i]);
        //}

        var texPath = AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(buddle, name)[0];

        Object[] obj = AssetDatabase.LoadAllAssetRepresentationsAtPath(texPath);

        if (typeof(T) != typeof(Object))
        {
            //返回类型转换
            T[] RetList = new T[obj.Length];
            int i = 0;

            foreach (Object o in obj)
            {
                RetList[i] = (T)o;
                i++;
            }
            return RetList;
        }
        else
            return (T[])obj;

#else
        AssetBundle ab = GetBundle(buddle);
        T[] unityAssets = ab.LoadAssetWithSubAssets<T>(name);
        return unityAssets;
#endif
    }

    /// <summary>
    /// 获取 muliti sprite 资源的子资源
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="buddle"></param>
    /// <param name="name">muliti资源名</param>
    /// <param name="subname">子资源名</param>
    /// <returns></returns>
    public static T GetSubByName<T>(string buddle, string name,string subname) where T : UnityEngine.Object
    {
        //Debug.Log("loadAll " + buddle +" " + name);
#if UNITY_EDITOR

        //string[] pathList = AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(buddle, name);
        //for (int i = 0; i < pathList.Length; i++)
        //{
        //    Debug.Log(pathList[i]);
        //}

        var texPath = AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(buddle, name)[0];

        Object[] obj = AssetDatabase.LoadAllAssetRepresentationsAtPath(texPath);




        foreach (Object o in obj)
        {
            if (o.name == subname)
            {

                return (T)o;
            }
        }

        return null;
#else
        AssetBundle ab = GetBundle(buddle);
        T[] unityAssets = ab.LoadAssetWithSubAssets<T>(name);
         foreach (Object o in unityAssets)
        {
            if (o.name == subname)
            {

                return (T)o;
            }
        }
        return null;
#endif
    }
}