项目急急封测。才发现之前偷懒资源预制都丢在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 } }