打包工具AssetBundleBrowser

工具展示:

image-20220708125221693

属性说明:

image-20220707231727922

AssetBundle加载资源

同步加载

同步加载assetbundle资源用分为以下两步

  1. 从指定目录中加载AB包
    AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "加载的ab包名字");
    

注意:

加载AB包,注意AB包不能重复加载,否则会报错

  1. 加载AB包中资源的三种方法
  • GameObject obj1 = ab.LoadAsset("Cube") as GameObject;//不推荐使用(会分不清同名的不同类型资源)
    
  • GameObject obj2 = ab.LoadAsset<GameObject>("Cube");
    
  • GameObject obj3 = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject;
    

异步加载

void Start()
{
    StartCoroutine(LoadABRes("image", "image"));
}
IEnumerator LoadABRes(string ABName, string resName)
{
    AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName);
    yield return abcr;
    AssetBundleRequest abq = abcr.assetBundle.LoadAssetAsync(resName, typeof(Sprite));
    yield return abq;
    Image.sprite = abq.asset as Sprite;
}

卸载所有加载的AB包:

注意ture会把加载的资源给卸载了,一般参数为false

AssetBundle.UnloadAllAssetBundles(false);

卸载单个包:

ab.Unload(false);

总结

  • 使用AssetBundle动态加载资源首先要获取AssetBundle对象,第二步才是从AssetBundle中加载目标资源。
  • 要在运行时加载AssetBundle对象主要可以分为两大类途径:
    • a先获取WWW对象,再通过WWW.assetBundle获取AssetBundle对象(已弃用)
    • b直接获取AssetBundle(5.3之后 LoadFromFile,LoadFromMemory,LoadFromMemoryAsync并增加了LoadFromFileAsync)
  • Unity提供了三个不同的API从AssetBundles加载UnityEngine.Objects,这些API都绑定到AssetBundle对象上,并且这些API具有同步和异步变体:
    • LoadAsset (LoadAssetAsync)
    • LoadAllAssets (LoadAllAssetsAsync)
    • LoadAssetWithSubAssets (LoadAssetWithSubAssetsAsync)
    • 并且这些API的同步版本总是比异步版本快至少一个帧(其实是因为异步版本为了确保异步,都至少延迟了1帧),异步加载每帧会加载多个对象,直到它们的时间切片切出。

AssetBundle加载依赖资源

为什么要加载依赖资源:

一个资源身上用到了别的AB包的资源,如果只加载了自己的AB包,会出现资源丢失的情况,需要把依赖包一起加载才会正常,如果重复加载又会报错

方法1:

  • 知道依赖于哪些包,直接加载
  • 实际开发中不可能知道依赖包,就算知道这样加载也十分的麻烦,不利于后期修改
AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "ui");
AssetBundle ab2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "image");
GameObject obj1 = ab.LoadAsset("Canvas", typeof(GameObject)) as GameObject;
Instantiate(obj1);

方法2:

  • 更加通用的方法,就算不知道资源依赖于哪些包,也可以通过下面方法获取依赖文件并进行加载
  • 如何操作:加载主包中的固定文件,从固定文件中得到依赖信息
//1.加载主包
AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "pc");
//2.加载主包中的固定文件
AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
//3.从固定文件中得到依赖信息
string[] strs = abManifest.GetAllDependencies("ui");
for (int i = 0; i < strs.Length; i++)
{
    AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + strs[i]);
}
//4.加载需要加载资源的包
AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "ui");
GameObject obj1 = ab.LoadAsset("Canvas", typeof(GameObject)) as GameObject;
Instantiate(obj1);

练习:AssetBundle管理器

工具类,提供同步、异步加载资源的三种方法方便使用

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class ABMgr : MonoSingleton<ABMgr>
{
    //开发目的:让外部更方便的进行资源加载
    //AB包不能重复加载,用字典来存储加载过的AB包

    private AssetBundle mainAB = null;
    private AssetBundleManifest mainfest = null;
    private Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();

    /// <summary>
    /// ab包存放路径
    /// </summary>
    private string PathUrl
    {
        get
        {
            return Application.streamingAssetsPath + "/";
        }
    }

    /// <summary>
    /// 主包名 根据平台不同选择不同的主包名
    /// </summary>
    private string MainName
    {
        get
        {
#if UNITY_IOS
            return "IOS";
#elif UNITY_ANDROID
            return "Android";
#else
            return "pc";
#endif
        }
    }

    /// <summary>
    /// 加载ab包
    /// </summary>
    /// <param name="abName"></param>
    private void LoadAB(string abName)
    {
        if (mainAB == null)
        {
            mainAB = AssetBundle.LoadFromFile(PathUrl + MainName);
            mainfest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }
        AssetBundle ab = null;
        string[] strs = mainfest.GetAllDependencies(abName);
        for (int i = 0; i < strs.Length; i++)
        {
            if (!abDic.ContainsKey(strs[i]))
            {
                ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
                abDic.Add(strs[i], ab);
            }
        }
        if (!abDic.ContainsKey(abName))
        {
            ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }
    }

    /// <summary>
    /// 同步加载
    /// 不指定类型
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    public Object LoadRes(string abName,string resName)
    {
        LoadAB(abName);
        Object obj  = abDic[abName].LoadAsset(resName);
        if (obj is GameObject)
        {
            return Instantiate(obj);
        }
        else
        {
            return obj;
        }
    }

    /// <summary>
    /// 同步加载
    /// 指定类型
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    public Object LoadRes(string abName, string resName, System.Type type)
    {
        LoadAB(abName);
        Object obj = abDic[abName].LoadAsset(resName,type);
        if (obj is GameObject)
        {
            return Instantiate(obj);
        }
        else
        {
            return obj;
        }
    }
    /// <summary>
    /// 同步加载
    /// 泛型
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public T LoadRes<T>(string abName, string resName) where T : Object
    {
        LoadAB(abName);
        T obj = abDic[abName].LoadAsset<T>(resName);
        if (obj is GameObject)
        {
            return Instantiate(obj);
        }
        else
        {
            return obj;
        }
    }

    /// <summary>
    /// 异步加载
    /// 不指定类型
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callBack"></param>
    public void LoadResAsync(string abName, string resName, UnityAction<Object> callBack)
    {
        StartCoroutine(IELoadResAsync(abName, resName, callBack));
    }
    private IEnumerator IELoadResAsync(string abName, string resName, UnityAction<Object> callBack)
    {
        LoadAB(abName);
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName);
        yield return abr;
        if (abr.asset is GameObject)
        {
            callBack(Instantiate(abr.asset));
        }
        else
        {
            callBack(abr.asset);
        }
    }

    /// <summary>
    /// 异步加载
    /// 指定类型
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="type"></param>
    /// <param name="callBack"></param>
    public void LoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callBack)
    {
        StartCoroutine(IELoadResAsync(abName, resName, type, callBack));
    }

    private IEnumerator IELoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callBack)
    {
        LoadAB(abName);
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName,type);
        yield return abr;
        if (abr.asset is GameObject)
        {
            callBack(Instantiate(abr.asset));
        }
        else
        {
            callBack(abr.asset);
        }
    }

    /// <summary>
    /// 异步加载
    /// 泛型
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callBack"></param>
    public void LoadResAsync<T>(string abName, string resName, UnityAction<T> callBack) where T : Object
    {
        StartCoroutine(IELoadResAsync<T>(abName, resName, callBack));
    }

    private IEnumerator IELoadResAsync<T>(string abName, string resName, UnityAction<T> callBack) where T : Object
    {
        LoadAB(abName);
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync<T>(resName);
        yield return abr;
        if (abr.asset is GameObject)
        {
            callBack(Instantiate(abr.asset) as T);
        }
        else
        {
            callBack(abr.asset as T);
        }
    }

    /// <summary>
    /// 单个包卸载
    /// </summary>
    /// <param name="abName"></param>
    public void UnLoad(string abName)
    {
        if (abDic.ContainsKey(abName))
        {
            abDic[abName].Unload(false);
            abDic.Remove(abName);
        }
    }

    /// <summary>
    /// 所有包卸载
    /// </summary>
    public void ClearAll()
    {
        AssetBundle.UnloadAllAssetBundles(false);
        abDic.Clear();
        mainAB = null;
        mainfest = null;
    }
}
using UnityEngine;
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    public bool global = true;
    static T instance;
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = (T)FindObjectOfType<T>();
            }
            return instance;
        }
    }
    /// <summary>
    /// 在monosingleton中写awake后
    /// 子类也不能使用awake了
    /// 同理如果要用start需要用重写的方法
    /// </summary>
    void Awake()
    {
        if (global)
        {
            if (instance != null && instance != this.gameObject.GetComponent<T>())
            {
                Destroy(this.gameObject);
                return;
            }
            DontDestroyOnLoad(this.gameObject);
            instance = this.gameObject.GetComponent<T>();
        }
        this.OnStart();
    }
    protected virtual void OnStart()
    {

    }
}

资料参考

https://www.bilibili.com/video/BV1LD4y1m7kF

https://zhuanlan.zhihu.com/p/38122862

https://zhuanlan.zhihu.com/p/97551363