Unity AssetBundle 分资源包管理和规范个人总结
项目急急封测。才发现之前偷懒资源预制都丢在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
}
}

浙公网安备 33010602011771号