3.使用AB包资源文件
3.1 知识点
同步加载AB包和AB包中的资源
AssetBundle.LoadFromFile:同步地从指定路径加载 AB 包,返回加载后的 AssetBundle。
//LoadFromFile 方法:同步地从指定路径加载 AB 包,返回加载后的 AssetBundle
//由于我们选择了StreamingAssets中多拷贝一份 可以直接从StreamingAssets加载AB包
AssetBundle modelAssetBundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");
//其他加载AB包的API不太常用 理解即可
//注意: 同一个AB包不能加载两次 否则报错
//The AssetBundle 'model' can't be loaded because another AssetBundle with the same files is already loaded.
//AssetBundle modelAssetBundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");//报错
AssetBundle.LoadAsset:同步地从一个已加载的 AB 包中加载指定资源
//LoadAsset 方法:同步地从一个已加载的 AB 包中加载指定资源
//GameObject cube = modelAssetBundle.LoadAsset("Cube") as GameObject;//假如只传名字可能会得到同名不同类型的资源 不建议使用
//GameObject cube = modelAssetBundle.LoadAsset<GameObject>("Cube");//泛型加载 可以使用 但是Lua中不支持
GameObject cube = modelAssetBundle.LoadAsset("Cube", typeof(GameObject)) as GameObject;//传入资源的Type 要as成实际类型 Lua中支持
Instantiate(cube);
//同一个AB包可以用于加载多个不同资源
GameObject sphere = modelAssetBundle.LoadAsset<GameObject>("Sphere");
Instantiate(sphere);
异步加载AB包和AB包中的资源
AssetBundle.LoadFromFileAsync 异步地从指定路径加载AB包,返回异步加载AB包的类
AssetBundle.LoadAssetAsync 异步地从一个已加载的AB包中加载指定资源
IEnumerator LoadAssetBundleAsync<T>(string AssetBundleName, string resourceName, Type resourceType)
{
//LoadFromFileAsync 方法:异步地从指定路径加载 AB 包,返回 AssetBundleCreateRequest
AssetBundleCreateRequest assetBundleCreateRequest = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + AssetBundleName);
yield return assetBundleCreateRequest;
//LoadAssetAsync 方法:异步地从一个已加载的 AB 包中加载指定资源
//和同步加载类似 异步加载也有三个重载
//异步加载AB包的类中的assetBundle属性代表已加载的AB包 返回异步加载AB包中的资源的类
//AssetBundleRequest assetBundleRequest = assetBundleCreateRequest.assetBundle.LoadAssetAsync(resourceName);//假如只传名字可能会得到同名不同类型的资源 不建议使用
AssetBundleRequest assetBundleRequest = assetBundleCreateRequest.assetBundle.LoadAssetAsync(resourceName, resourceType);//传入资源的Type
//AssetBundleRequest assetBundleRequest = assetBundleCreateRequest.assetBundle.LoadAssetAsync<T>(resourceName);//泛型加载 可以使用 但是Lua中不支持
yield return assetBundleRequest;
//异步加载AB包中的资源的类中的asset属性代表实际加载出的资源 要as成实际类型
image.sprite = assetBundleRequest.asset as Sprite;
}
使用协程进行异步加载
//开启协程 一定要注意传进来的Type对不对
StartCoroutine(LoadAssetBundleAsync<Sprite>("icon", "quanlity_0",typeof(Sprite)));
卸载AB包和AB包中的资源
AssetBundle.Unload 卸载指定的AB包 传入的参数是是否卸载场景上加载出来的AB包资源
//Unload方法 卸载指定的AB包 传入的参数是是否卸载场景上加载出来的AB包资源
modelAssetBundle.Unload(false);
AssetBundle.UnloadAllAssetBundles 卸载所有已加载的AB包 传入的参数是是否卸载场景上加载出来的AB包资源。
//UnloadAllAssetBundles方法 卸载所有已加载的AB包 传入的参数是是否卸载场景上加载出来的AB包资源
AssetBundle.UnloadAllAssetBundles(false);
拓展:LoadAsset(“Cube”) 为什么能直接用短名?包内真名是什么?
上面同步加载里传的是 "Cube"、"Sphere" 这种短名,不是工程里的 Assets/xxx/Cube.prefab,也不是 Application.dataPath 那种路径。入门 Demo 这么写能跑,容易让人以为 AB 包认的是 Inspector 里的资源路径——其实不是。
短名为什么常常能用
- 我们前面把预制体命名为
Cube、Sphere,打进 model 包后,LoadAsset传"Cube"往往就能取到,本质是 Unity 在当前已打开的这个 AB 包里做名字匹配,文件名对得上就行。 - 同一个包里有多个叫法时,所以笔记里才强调最好带上
typeof(GameObject),别裸传字符串。否则可能加载的不匹配。 - Windows 上大小写有时比较松,
Cube和cube可能都加载成功;换 Android、Linux 可能会失败,以打出来的包为准。
包里面真正登记的名字:GetAllAssetNames()
短名能加载,不代表包内只有短名。AB 包加载起来之后,可以用下面代码把这个包里所有资源的真名打出来:
AssetBundle modelAssetBundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");
string[] names = modelAssetBundle.GetAllAssetNames();
for (int i = 0; i < names.Length; i++)
{
Debug.Log(names[i]);
}
常见会看到类似 assets/xxx/cube.prefab 这种小写 + 带目录的字符串——这才是 Unity 写进包里的 canonical 名字。和你在 Project 窗口里看到的路径不一定一模一样(大小写、有没有 Assets/ 前缀都可能不同)。
所以:LoadAsset 传 "Cube" 是入门写法;GetAllAssetNames() 看到的才是排查问题时该信的字符串。 加载失败、换 Unity 版本、改目录结构后,最好先打一遍 log 排查问题。
和工程路径是两回事
LoadAsset 不能直接传 Assets/Prefabs/Cube.prefab。运行时没有给我工程路径,Unity 自动找 AB 包这种 API。所以如果想实现这样的通过工程路径加载的功能,得先知道:
- 资源在哪个 AB 包(比如
model) - 在这个包里用什么字符串
LoadAsset(短名或 GetAllAssetNames 里那个)
后面做简单的 AB 包管理器 用 (包名, 资源名) 把这两步包了一层,已经是的小清单了。但是实际商业级项目中可能还不够的。
想稳定按路径 / 地址加载,还得再封一层
Demo 里 (model, "Cube") 做Demo时够用;项目资源一多、要热更、路径会变,光靠短名就不稳定了。比较靠谱的做法是:打包时生成一张表(清单),运行时只认你定好的 key,例如:
- 工程路径风格的键:
Assets/Prefabs/Cube.prefab - 或者业务地址:
UI/Login/LoginPanel(后面 YooAsset、Addressables 常见这种)
表里至少要能查到:在哪个 AB 包、包内 LoadAsset 用什么名、依赖哪些包。自己写 Manager + 导出 JSON 也行;不想手写就导入第三方框架,例如 YooAsset / Addressables。思路都是差不多的,都是清单 + 地址加载,只是框架可能帮你把构建、依赖、热更那套也做了。
3.2 知识点代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Lesson03_使用AB包资源文件 : MonoBehaviour
{
public Image image;
void Start()
{
#region 知识点一 同步加载AB包和AB包中的资源
//LoadFromFile 方法:同步地从指定路径加载 AB 包,返回加载后的 AssetBundle
//由于我们选择了StreamingAssets中多拷贝一份 可以直接从StreamingAssets加载AB包
AssetBundle modelAssetBundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");
//其他加载AB包的API不太常用 理解即可
//注意: 同一个AB包不能加载两次 否则报错
//The AssetBundle 'model' can't be loaded because another AssetBundle with the same files is already loaded.
//AssetBundle modelAssetBundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");//报错
//LoadAsset 方法:同步地从一个已加载的 AB 包中加载指定资源
//GameObject cube = modelAssetBundle.LoadAsset("Cube") as GameObject;//假如只传名字可能会得到同名不同类型的资源 不建议使用
//GameObject cube = modelAssetBundle.LoadAsset<GameObject>("Cube");//泛型加载 可以使用 但是Lua中不支持
GameObject cube = modelAssetBundle.LoadAsset("Cube", typeof(GameObject)) as GameObject;//传入资源的Type 要as成实际类型 Lua中支持
Instantiate(cube);
//同一个AB包可以用于加载多个不同资源
GameObject sphere = modelAssetBundle.LoadAsset<GameObject>("Sphere");
Instantiate(sphere);
#endregion
#region 知识点二 异步加载AB包和AB包中的资源
//开启协程 一定要注意传进来的Type对不对
StartCoroutine(LoadAssetBundleAsync<Sprite>("icon", "quanlity_0",typeof(Sprite)));
#endregion
#region 知识点三 卸载AB包和AB包中的资源
//Unload方法 卸载指定的AB包 传入的参数是是否卸载场景上加载出来的AB包资源
modelAssetBundle.Unload(false);
//UnloadAllAssetBundles方法 卸载所有已加载的AB包 传入的参数是是否卸载场景上加载出来的AB包资源
AssetBundle.UnloadAllAssetBundles(false);
#endregion
}
#region 知识点二 异步加载AB包和AB包中的资源
IEnumerator LoadAssetBundleAsync<T>(string AssetBundleName, string resourceName, Type resourceType)
{
//LoadFromFileAsync 方法:异步地从指定路径加载 AB 包,返回 AssetBundleCreateRequest
AssetBundleCreateRequest assetBundleCreateRequest = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + AssetBundleName);
yield return assetBundleCreateRequest;
//LoadAssetAsync 方法:异步地从一个已加载的 AB 包中加载指定资源
//和同步加载类似 异步加载也有三个重载
//异步加载AB包的类中的assetBundle属性代表已加载的AB包 返回异步加载AB包中的资源的类
//AssetBundleRequest assetBundleRequest = assetBundleCreateRequest.assetBundle.LoadAssetAsync(resourceName);//假如只传名字可能会得到同名不同类型的资源 不建议使用
AssetBundleRequest assetBundleRequest = assetBundleCreateRequest.assetBundle.LoadAssetAsync(resourceName, resourceType);//传入资源的Type
//AssetBundleRequest assetBundleRequest = assetBundleCreateRequest.assetBundle.LoadAssetAsync<T>(resourceName);//泛型加载 可以使用 但是Lua中不支持
yield return assetBundleRequest;
//异步加载AB包中的资源的类中的asset属性代表实际加载出的资源 要as成实际类型
image.sprite = assetBundleRequest.asset as Sprite;
}
#endregion
#region 知识点三 卸载AB包和AB包中的资源
private void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
//UnloadAllAssetBundles方法 卸载所有已加载的AB包 传入的参数是是否卸载场景上加载出来的AB包资源
AssetBundle.UnloadAllAssetBundles(true);
}
}
#endregion
}
3.3 练习题
如何解决AB包不允许重复加载的问题?
用字典记录已加载的AB包,如果存在则不重复加载。如果要记录,那么一定要提供卸载的方法。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com