3.使用AB包资源文件

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 里的资源路径——其实不是

短名为什么常常能用

  • 我们前面把预制体命名为 CubeSphere,打进 model 包后,LoadAsset"Cube" 往往就能取到,本质是 Unity 在当前已打开的这个 AB 包里做名字匹配,文件名对得上就行。
  • 同一个包里有多个叫法时,所以笔记里才强调最好带上 typeof(GameObject),别裸传字符串。否则可能加载的不匹配。
  • Windows 上大小写有时比较松,Cubecube 可能都加载成功;换 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。所以如果想实现这样的通过工程路径加载的功能,得先知道:

  1. 资源在哪个 AB 包(比如 model
  2. 在这个包里用什么字符串 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

×

喜欢就点赞,疼爱就打赏