【教程】Unity AssetBundle 资源管理方法

开发平台:Unity 2020版本以上
编程平台:Visual Studio 2022

一、前言


  在 Unity 中,“资源管理与使用” 是开发者必修内容。受限制于 项目目标平台、用户设备类型 等因素,资管管理方案不一。本文重点记录 Unity - AssetBundle 资源加载模式。

二、Resourse 与 AssetBundle


2.1 Resources 资源管理方式

  1. 无法对 Component、GameObject 等对象做到 “即用即卸载”,最终将影响运行时内存占用与其他性能开销。
  2. Resources 内所有资产将被 Unity 构建过程中打入应用体量内,对于大型资产的项目中进行资产更新将是困难的。

2.2 选择 AssetBundle 的理由

  • 有效解决商业项目中初次 "下载体量大"的特点。
    • 参考《小鳄鱼爱洗澡》以抢先体验前几关,后续游玩采取 "逐关下载,快速体验" 的策略。
      该策略让用户无需等待太长时间即可体验内容,进而更可能保留用户群体。
  • 设备内存管理优异,设备表现上优异。

三、资产加载 | AssetBundle


1)本地加载 AssetBundle 资产

string path = $"{Application.streamingAssetsPath}/Cube";
string modelName = "cube";

public void Start() {
	AssetBundle thisAB = AssetBundle.LoadFromFile(path);
	GameObject thisAsset = thisAB.LoadAsset<GameObject>(modelName );
	MonoBehaviour.Instantiate(thisAsset);
}

2)使用 UnityWebRequestAssetBundle 加载 AssetBundle 资源

string downloadURL = $"  ---------------------------------------   ";
string modelName = $" ---------------------- ";

private IEnumerator Start() {
	var thisRequst = UnityWebRequestAssetBundle.GetAssetBundle(downloadURL);
	yield return thisRequest.SendWebReqeust();
	
	AssetBundle thisBundle = DownLoadHandlerAssetBundle.GetContent(thisRequest);
	var thisLoadAsset = bundle.LoadAssetAsync<GameObject>(modelName);
	yield return loadAsset;

	Instantiate(loadAsset.asset);
}

3)异步加载资源

在这里插入代码片

四、依赖资产 | AssetBundle


在修改部分资源内容时,会出现资源依赖关系丢失的问题,例如 Material、Mesh 等。为防止各资源包中的引用丢失情况,提出加载前优先加载关联资源后再加载本资源对象。

除游戏对象生成的 assetbundle 文件,还会额外生成该目录下的 assetbundle 文件。该文件记录本次输出 assetbundle 对象群的所有外部依赖关联信息。例如在 StreamingAssets 目录下输出 Cube 的 assetbundle 文件,会额外产出命名 StreamingAssets.aseebundle 文件。

示例代码:

string manifestPath = $"{Application.streamingAssetsPath}/StreamingAssets";
string path = $"{Application.streamingAssetsPath}/cube";
string modelName = "cube"

void Start() {
	// 加载依赖
	AssetBundle thisABDep = AssetBundle.LoadFromFile(manifestPath);
	var manifest = thisABDep.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
	string[] dependcies = manifest.GetAllDependencies("cube");

	Array.ForEach(dependcies, e => AssetBundle.LoadFromFile($"{Application.streamingAssetsPath}/{e}"));

	// 加载模型
	var assetGameObject = thisAB.LoadAsset<GameObject>("cube");
	MonoBehaviour.Instantiate(assetGameObject);

	// 加载资产依赖资产
	Array.ForEach(dependcies, e => AssetBundle.LoadFromFile(e));
}

图1 Assetbundle 生成目录
图2 Aseetbundle 目录下所有AB的依赖关联信息

4.1 加载 Manifest

AssetBundle thisABDep = AssetBundle.LoadFromFile(manifestPath);
  • manifestPath 是 AssetBundle - Browsers 本次导出所有资产的依赖关系文件。
  • 该 Manifest 文件名以存储目录文件名为命名,并规定存储在该目录文件夹内。

4.2 AssetBundleManifest | 跳转

var manifest = thisABDep.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
  • 这段代码获取依赖信息下关于 AssetBundleManifest 的信息内容,详细参考上 图2。

五、资产加载 | 流程


5.1 关于存储数据结构

  使用环境中,多为单场景跳转时对上一场景的加载资源进行卸载,并准备当前场景的资源加载。而 AssetBundle 包加载 禁止重复加载。为管理与校验是否多次加载。需要定义数据结构管理已加载的 AssetBundle 对象。

public Dictionary<string, AssetBundle> AssetBundleDic { get; set; } = new();
  • 此处使用 Dictionary 作为 AssetBundle 管理

5.2 关于主要包体(可选)

public AssetBundle MainAssetBundle;

  即所有 AssetBundle 的依赖关系包对象。参考 [本博文] 4.2 AssetBundleManifest 的解释。 该 AssetBundle 包体仅需加载一次即可。故作为单独使用。当然通过 Dictionary 中判断是否加载也可。

5.3 关于多平台下包名差异

public string MainAssetBundle {
	get {
#if UNITY_IOS
		return "Ios";
#elif UNITY_ANDROID
		return "Android";
#else 
		return "Windows"
#endif
	}
}
  1. 对于多平台发布会涉及 AssetBundle 不一的情况,考虑主依赖包信息的命名差别,使用 宏定义
  2. 参考 [本博文] 4.1 加载Manifest 加载所有依赖项。

5.4 关于卸载

当 GameObject 被实例化至场景后,该 AssetBundle 内资产将被记入内存中。执行 UnLoad 后,并不影响已实例化在场景内的所有资源。

### UnityAssetBundle资源管理方法 #### 创建与打包 AssetBundleUnity 中,AssetBundle 是一种用于管理和分发游戏资产的技术。通过将纹理、模型和其他资源打包成 AssetBundles,可以在运行时按需加载这些资源,从而提高应用启动速度并降低初始安装大小[^1]。 要创建 AssetBundle 文件,在编辑器内选择想要打包的对象,并为其指定一个唯一的名称作为标签。接着利用 `BuildPipeline.BuildAssetBundles` 方法来构建实际的 Bundle 文件。此过程会遍历场景中的所有标记对象并将它们编译为独立于平台的数据文件。 ```csharp using UnityEngine; using UnityEditor; public class BuildAB : MonoBehaviour { static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths){ foreach (string asset in importedAssets) { if(asset.Contains(".prefab") || asset.Contains(".fbx")) { // 自动打 AB 包逻辑 AssetImporter ai = AssetImporter.GetAtPath(asset); ai.assetBundleName = Path.GetFileNameWithoutExtension(asset)+".assetbundle"; } } } [MenuItem("Tools/Build All AssetBundles")] public static void BuildAllAssetBundles() { string path = "Assets/StreamingAssets/"; if (!Directory.Exists(path)) Directory.CreateDirectory(path); BuildPipeline.BuildAssetBundles( path, BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget ); } } ``` #### 存储与下载 AssetBundle 对于已经生成好的 AssetBundle 文件,通常会被上传至服务器供客户端请求获取。当玩家首次访问某个特定内容时,应用程序可以从远程位置拉取必要的数据;之后则可以直接从本地缓存读取已有的副本以加快响应时间。这不仅有助于节省带宽成本,还能显著提升用户体验的质量。 为了实现这一点,可以借助 HTTP 协议下的 Web 请求功能完成异步下载操作: ```csharp IEnumerator LoadAssetBundleAsync(string url) { using(var www = new WWW(url)){ yield return www; if(www.error != null){ Debug.LogError($"Failed to load bundle from '{url}': {www.error}"); yield break; }else{ var ab = www.assetBundle; GameObject go = Instantiate(ab.LoadAsset<GameObject>("MyPrefab")); // Do something with the loaded object... ab.Unload(false); // Unloads only the assets that are not used elsewhere. } } } ``` #### 加载与卸载 AssetBundle 一旦成功下载了所需的 AssetBundle 数据包,就可以调用相应的 API 来实例化其中包含的游戏物体或其他类型的资源。需要注意的是,在不再需要某些资源的时候应当及时释放其占用的空间,以免造成不必要的内存浪费。可以通过设置参数控制是否保留未被其他地方使用的依赖项。 此外,定期清理过期或不常用的 AssetBundle 可以进一步优化系统的整体表现。为此,建议采用类似于 LRU(Least Recently Used)这样的策略来进行有效的垃圾回收处理。 #### 最佳实践总结 - **定期检测**:每次打包 AssetBundle 后应立即执行冗余检查,确保资源的有效性和最佳状态。 - **自动化集成**:考虑把 AssetBundle Reporter 工具融入持续集成环境中,以便自动分析潜在问题并向相关人员汇报结果。 - **资源分类**:依据不同类型和使用频次对资源进行合理的划分,以此减少重复现象的发生几率[^2].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值