当前位置: 首页 > article >正文

Unity AssetBundles(AB包)

目录

前言

AB包是什么

AB包有什么作用

1.相对Resources下的资源AB包更好管理资源

2.减小包体大小

3.热更新

官方提供的打包工具:Asset Bundle Browser

AB包资源加载

 AB包资源管理模块代码


前言

在现代游戏开发中,资源管理是一项至关重要的任务。随着游戏内容的日益丰富和复杂,如何高效地加载、管理和卸载游戏资源成为了开发者们必须面对的挑战。Unity,作为一款广泛使用的游戏引擎,提供了一套强大的资源管理方案——AssetBundles(简称AB包)。

AB包是什么

特定于平台的资产压缩包,有点类似压缩文件
资产包括:模型、贴图、预设体、音效、材质球等等


AB包有什么作用

1.相对Resources下的资源AB包更好管理资源

2.减小包体大小

1.压缩资源
2.减少初始包大小

3.热更新

资源热更新
脚本热更新


官方提供的打包工具:Asset Bundle Browser

将以下脚本放置在Editor文件夹中

using UnityEditor;
using UnityEngine;
 
public class CreateAssetBundles
{
    [MenuItem("Assets/Build AssetBundles")]
    static void BuildAllAssetBundles()
    {
        string assetBundleDirectory = "Assets/AssetBundles";
        if (!System.IO.Directory.Exists(assetBundleDirectory))
        {
            System.IO.Directory.CreateDirectory(assetBundleDirectory);
        }
        BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
    }
}

创建AB包,选中资源,在Asset Bundle里选择New
我这里取名为icon

点击Asset/Build AssetBundles

这样就会在AssetBundles文件夹里生成一些资源文件

AB包文件:资源文件
manifest文件:AB包文件信息;当加载时,提供了关键信息,资源信息,依赖关系,版本信息等等
关键AB包(和目录名一样的包):主包,AB包依赖关键信息

AB包资源加载

将脚本挂载到Image上,启动项目,资源会从ab包中加载到sprite上

using UnityEngine;
using UnityEngine.UI;

public class ABTest : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        //第一步 加载 AB包
        AssetBundle ab= AssetBundle.LoadFromFile("Assets/AssetBundles/icon");
        //第二步 加载 AB包中的资源
        //只是用名字加载 会出现 同名不同类型资源 分不清
        //建议用泛型或Type指定类型
        Sprite icon = ab.LoadAsset("item_0", typeof(Sprite)) as Sprite;

        GetComponent<Image>().sprite = icon;
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

协程加载

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class ABTest : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        StartCoroutine(LoadABRes("item_0"));
    }
    IEnumerator LoadABRes(string resName)
    {
        AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync("Assets/AssetBundles/icon");
        yield return abcr;
        //加载资源
        AssetBundleRequest abq= abcr.assetBundle.LoadAssetAsync(resName,typeof(Sprite));
        yield return null;
        GetComponent<Image>().sprite = abq.asset as Sprite;
    }
    
}

卸载AB包

//卸载所有加载的AB包 参数为ture 会把通过AB包加载的资源也卸载了AssetBundle.UnloadAllAssetBundles(false);

关于AB包的依赖,一个资源身上用到了别的AB包中的资源,这个时候,如果只加载自己的AB包,通过它创建对象会出现资源丢失的情况,这种时候 需要把依赖包 一起加载了 才能正常。

依赖包的固定写法

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class ABTest : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        //第一步 加载 AB包
        AssetBundle ab = AssetBundle.LoadFromFile("AB包文件路径");
        //依赖包的关键知识点一利用主包 获取依赖信息
        //加载主包
        AssetBundle abMain = AssetBundle.LoadFromFile("主包文件路径");
        //加载主包中的固定文件
        AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest> ("AssetBundleManifest");
        //从固定文件中 得到依赖信息
        string[] strs = abManifest.GetAllDependencies("AB包名");
        for (int i = 0; i < strs.Length; i++) 
        {
            AssetBundle.LoadFromFile("前置路径/" + strs[i]);
        }
    }


}

 AB包资源管理模块代码

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

public class ABMgr : MonoBehaviour
{
    private static ABMgr instance;
    public static ABMgr GetInstance()
    {
        if (instance == null)
        {
            GameObject obj = new GameObject();
            //设置对象的名字为脚本
            obj.name = typeof(GameObject).ToString();
            instance = obj.AddComponent<ABMgr>();
        }
        return instance;
    }


    //主包
    private AssetBundle mainAB = null;
    //主包依赖获取配置文件
    private AssetBundleManifest manifest = null;

    //选择存储 AB包的容器
    //AB包不能够重复加载 否则会报错
    //字典知识 用来存储 AB包对象
    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>
    /// 加载主包 和 配置文件
    /// 因为加载所有包是 都得判断 通过它才能得到依赖信息
    /// 所以写一个方法
    /// </summary>
    private void LoadMainAB()
    {
        if (mainAB == null)
        {
            mainAB = AssetBundle.LoadFromFile(PathUrl + MainName);
            manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }
    }

    /// <summary>
    /// 加载指定包的依赖包
    /// </summary>
    /// <param name="abName"></param>
    private void LoadDependencies(string abName)
    {
        //加载主包
        LoadMainAB();
        //获取依赖包
        string[] strs = manifest.GetAllDependencies(abName);
        for (int i = 0; i < strs.Length; i++)
        {
            if (!abDic.ContainsKey(strs[i]))
            {
                AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
                abDic.Add(strs[i], ab);
            }
        }
    }

    /// <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
    {
        //加载依赖包
        LoadDependencies(abName);
        //加载目标包
        if (!abDic.ContainsKey(abName))
        {
            AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }

        //得到加载出来的资源
        T obj = abDic[abName].LoadAsset<T>(resName);
        //如果是GameObject 因为GameObject 100%都是需要实例化的
        //所以我们直接实例化
        if (obj is GameObject)
            return Instantiate(obj);
        else
            return obj;
    }

    /// <summary>
    /// Type同步加载指定资源
    /// </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)
    {
        //加载依赖包
        LoadDependencies(abName);
        //加载目标包
        if (!abDic.ContainsKey(abName))
        {
            AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }

        //得到加载出来的资源
        Object obj = abDic[abName].LoadAsset(resName, type);
        //如果是GameObject 因为GameObject 100%都是需要实例化的
        //所以我们直接实例化
        if (obj is GameObject)
            return Instantiate(obj);
        else
            return obj;
    }

    /// <summary>
    /// 名字 同步加载指定资源
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public Object LoadRes(string abName, string resName)
    {
        //加载依赖包
        LoadDependencies(abName);
        //加载目标包
        if (!abDic.ContainsKey(abName))
        {
            AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }

        //得到加载出来的资源
        Object obj = abDic[abName].LoadAsset(resName);
        //如果是GameObject 因为GameObject 100%都是需要实例化的
        //所以我们直接实例化
        if (obj is GameObject)
            return Instantiate(obj);
        else
            return obj;
    }

    /// <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(ReallyLoadResAsync<T>(abName, resName, callBack));
    }
    //正儿八经的 协程函数
    private IEnumerator ReallyLoadResAsync<T>(string abName, string resName, UnityAction<T> callBack) where T : Object
    {
        //加载依赖包
        LoadDependencies(abName);
        //加载目标包
        if (!abDic.ContainsKey(abName))
        {
            AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }
        //异步加载包中资源
        AssetBundleRequest abq = abDic[abName].LoadAssetAsync<T>(resName);
        yield return abq;

        if (abq.asset is GameObject)
            callBack(Instantiate(abq.asset) as T);
        else
            callBack(abq.asset as T);
    }

    /// <summary>
    /// Type异步加载资源
    /// </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(ReallyLoadResAsync(abName, resName, type, callBack));
    }

    private IEnumerator ReallyLoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callBack)
    {
        //加载依赖包
        LoadDependencies(abName);
        //加载目标包
        if (!abDic.ContainsKey(abName))
        {
            AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }
        //异步加载包中资源
        AssetBundleRequest abq = abDic[abName].LoadAssetAsync(resName, type);
        yield return abq;

        if (abq.asset is GameObject)
            callBack(Instantiate(abq.asset));
        else
            callBack(abq.asset);
    }

    /// <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(ReallyLoadResAsync(abName, resName, callBack));
    }

    private IEnumerator ReallyLoadResAsync(string abName, string resName, UnityAction<Object> callBack)
    {
        //加载依赖包
        LoadDependencies(abName);
        //加载目标包
        if (!abDic.ContainsKey(abName))
        {
            AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }
        //异步加载包中资源
        AssetBundleRequest abq = abDic[abName].LoadAssetAsync(resName);
        yield return abq;

        if (abq.asset is GameObject)
            callBack(Instantiate(abq.asset));
        else
            callBack(abq.asset);
    }

    //卸载AB包的方法
    public void UnLoadAB(string name)
    {
        if (abDic.ContainsKey(name))
        {
            abDic[name].Unload(false);
            abDic.Remove(name);
        }
    }

    //清空AB包的方法
    public void ClearAB()
    {
        AssetBundle.UnloadAllAssetBundles(false);
        abDic.Clear();
        //卸载主包
        mainAB = null;
    }
}


http://www.kler.cn/a/421288.html

相关文章:

  • Linux命令行解释器的模拟实现
  • Hutool:Java开发者的瑞士军刀
  • Linux权限机制深度解读:系统安全的第一道防线
  • 从单一设备到万物互联:鸿蒙生态崛起的未来之路
  • Ai编程cursor + sealos + devBox实现登录以及用户管理增删改查(十三)
  • 游戏引擎学习第25天
  • stm32 spi接口传输asm330l速率优化(及cpu和dma方式对比)
  • 威联通-001 手机相册备份
  • docker.io连接超时的处理,用代理网站
  • 接口隔离原则理解和实践
  • 计算机网络-网络安全
  • 游戏引擎学习第31天
  • k8s 资源管理resourceQuota
  • Luma AI技术浅析(五):GAN 改进技术
  • qemu 9.1.2 源码编译
  • ffmpeg拼接两个视频的一种可行方法
  • Android10 设备死机的问题分析和解决
  • python使用python-docx处理word
  • brynet源码阅读——http组件和wrapper组件
  • Reinforcement Learning with Human in the Loop Human Feedback
  • 【漏洞复现】海信智能公交企业管理系统 apply.aspx SQL注入漏洞
  • SIMD与SIMT
  • 刷leetcode hot100--3贪心(30min,看思路)
  • 【数据结构】【线性表】特殊的线性表-字符串
  • Android中使用NSD扫描,实现局域网内设备IP的发现
  • 第1章:CSS简介 --[CSS零基础入门]