【Unity学习笔记】AssetBundle

news2024/9/29 19:22:05

文章目录

  • 什么是AB包?
    • 为什么使用AB包?
  • 如何导出AB包
    • AB包导出文件
  • 如何使用AB包
    • AB包的加载
      • 同步加载
      • 异步加载
    • AB包的卸载
    • 依赖加载
  • AB包资源管理器


什么是AB包?

AssetBundle是Unity提供的一种用于存储资源的压缩集合,它可以存储任何一种Unity可以识别的资源,如模型,纹理图,音频,场景等资源。也可以加载开发者自定义的二进制文件。

为什么使用AB包?

Resources打包时会将所有的资源统统打包到Resources中,这意味着即使你只想要其中的部分资源,也需要对所有资源全部打包带走;并且由于它是只读的,所以我们不能随意地修改包的内容。

相对的使用AB包能够分别将不同的资源打包到独立的包体内,AB包存储路径可自定义,压缩方式可自定义,后期也可以对已经打包的内容进行动态更新。

AB包可以节约包体资源大小。一方面在压缩时AB包允许更好的压缩方式,以及选取的压缩资源也可以随意选择。另一方面,例如在游戏客户端的时候,由于AB包是可修改的,因此可以减少初始的安装大小,在安装游戏的时候我们可以设置一个可供游戏运行的较小的AB包,当玩家更新时则可以把其余的AB包内容下载完。

AB包还可以方便我们进行热更新,例如玩王者的时候,我们并没有下载更新,而游戏界面却更新了。这可以通过游戏内部对已经下载的AB包资源进行热更新而实现。而在游戏需要更新下载的时候,通常是先让客户端与服务器通信后,获取资源服务器的地址,然后和资源服务器的AB包进行对比来判断需要更新的资源。


如何导出AB包

在安装了AB包服务后如何导出AB包?

1.选中物体,在Inspector界面下方输入(选择)绑定的包体名称
2.在AssetBundles面板查看已经打的包
3.选择导出选项

此处介绍一下导出选项

属性选项
Build Target选项对应导出的平台
Output导出路径,AB包根路径AssetBundles与Asset同级
Clear Folders清空导出文件夹
Copy to StreamingAssets导出完毕后,导出的AB包复制到Assets文件夹中的StreamingAssets中

一些比较重要的参数

属性选项
Compression压缩模式:No Compression无压缩,不用解包,优点是读取快,但是文件大
LZMA标准压缩,压缩率高,但解包麻烦
LZ4区块压缩,当使用包内的资源时,只会解压对应的chunk,内存占用率低, 强烈推荐
Exclude Type Information不包含资源类型信息
Force Rebuild强制重构包内所有内容
Ignore Type Tree Changes在执行增量构建检查时忽略类型树更改
Append HashassetBundle 名称附加哈希(哈希寻址,文件校验)
Strict Mode如果在此期间报告任何错误,则构建无法成功
Dry Run Build进行干运行构建。此选项允许您针对 AssetBundle 进行干运行构建,但不实际构建它们。启用此选项后,BuildPipeline.BuildAssetBundles 仍将返回 AssetBundleManifest 对象,其中包含有效的 AssetBundle 依赖关系和哈希。

此外在unity中,C#是不能打包成AB包的,因此我们需要学习Lua(哈哈,太搞笑了)。打包成AB包时物体上的C#脚本其实只包含了对应FileID的映射。

4.在Inspect中打开对应资源可以检查导出完毕的AB包中的信息

此外,被打包的物体所使用的那些资源,例如shader,sprite等,即使没有主动将它们打包,它们也会被自动地打包(并且默认与使用它的物体在同一个包体中)。此时我们可以在AssetLabel里选择它属于哪个包。但是要注意,如果物体所依赖的资源被打到其他的包中,那么单独加载物体往往会造成其他依赖资源的丢失,因此在加载这个物体的时候我们还需要同时加载它的依赖包。

AB包导出文件

在这里插入图片描述

打包完成后,每个包会对应生成一个包和一个对应的manifest清单,此外还有一个与文件夹同名的包,称为主包,它储存了包与包之间的依赖关系的信息。所有的包没有后缀名,是二进制文件。Manifest包含了资源中的信息。

我们往往会勾选StreamingAssets,这是为了方便在代码中进行调用,通常使用:

Application.streamingAssetsPath + "/" +"包名"

来读取AB包的路径


如何使用AB包

AB包的加载

AB包的加载分为两步,第一步先加载AB包,第二步加载包内对应的资源

同步加载

最简单的方法就是同步的方式加载AB包资源,所有代码全部依次执行

首先加载AB包

AssetBundle AB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "包名");

然后加载资源,资源加载有两种方式,一种通过泛型加载,另一种直接加载并指定类型。

// T对应加载的资源的类型
T obj = AB.LoadAsset<T>("包内资源名");
// T对应加载的资源的类型
T obj = AB.LoadAsset("包内资源名", typeof(T)) as T;

从实用性上来说第一种加载方式好,但是从实践上来说第二种更好,因为Lua不支持泛型。


在这种情况下

异步加载

异步加载是我们推荐的方式,因为在Unity是单线程的,我们不希望同步加载时由于资源数量多导致的主线程阻塞,这会造成游戏卡死。说到异步加载,那第一个肯定想到的是协程,除了用协程加载,AB包也提供了一些异步加载方法。

    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为例
        Sprite.sprite = abq.asset as Sprite;
    }

AB包的卸载

AssetBundle.UnloadAllAssetBundles(true);
AssetBundle.UnloadAllAssetBundles(false);

UnloadAllAssetBundles方法可以一键卸载全部的AB包,其中包含两种模式,true模式下会直接卸载场景中的AB包,这会导致场景资源丢失。所以推荐使用false,false时会卸载全部AB包并且场景资源不丢失。

同样的我们可以用Unload方法卸载指定AB包,同样的true和false。

AB.Unload(true);
AB.Unload(false);

依赖加载

如果一个模型依赖一个资源,我们可以将两个包都加载。但是如果一个模型依赖好多资源,难道我们要手动加载这么多包?更糟糕的是如果你不知道依赖包是哪个该怎么办?

因此我们需要主包来获取依赖信息

// 加载主包
AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "主包包名");
// 获取主包清单
AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
// 从清单中获取依赖信息并保存在一个字符串数组中
string[] strs = abManifest.GetAllDependencies("需要加载的包名");
// for循环或者foreach都可以循环加载依赖包
foreach (string str in strs)
{
	AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + str);
}

有个问题,现在我们的确加载了使用包体的资源。但是如果在另一个包中,它的依赖包和这个包的依赖包是相同的,那么如果我们加载了另一个包,就会报错!因为同一个依赖包被加载了两次。

为了避免这种问题,我们需要一个管理器来统一管理资源包的加载卸载!


AB包资源管理器

首先我们思考,一个资源管理器应当有哪些特点:

  • 全局唯一,否则其他类的调用依旧会导致重复加载(单例模式)
  • 字典存储,列出一个包体使用清单,并且键值对能避免重复写入
  • 异步加载,保证主线程安全

那么一个单例模式的资源管理器将是最佳的选择。

主要过程分为四步:

  • 写一个函数实现读取主包获取依赖关系
  • 定义一个字典以键值对形式存储包名和AB包资源之间的映射,字典可以避免重复读取同一个包
  • 重写Unity的读取AB包方法,使之先读取主包获取所读AB包的依赖关系,随后读取该AB包和所有依赖包
  • 定义卸载方法,清空字典
  • (以及其他的自定义可拓展的方法)

这里仅给出一小段代码:

定义一个DontDestroyOnLoad的接受泛型的单例基类

/// <summary>
/// 该单例继承于Mono,并且不会随着场景的切换而销毁
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class DDOLSingletonMono<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;
    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<T>();
                if (_instance == null)
                {
                    GameObject obj = new GameObject();
                    obj.name = typeof(T).Name;
                    DontDestroyOnLoad(obj);
                    _instance = obj.AddComponent<T>();
                }
            }
            return _instance;
        }
    }
}

定义一个ABManager,它继承了上述基类,并在其内定义管理方法

    /// <summary>
    /// 加载AB包
    /// </summary>
    /// <param name="abName"></param>
    public void LoadAB(string abName)
    {
        // 获取主包
        if (mainAB == null)
        {
            mainAB = AssetBundle.LoadFromFile(PathUrl + MainABName);
            manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }
        AssetBundle ab = null;
        // 获取所用资源包依赖信息
        string[] relys = manifest.GetAllDependencies(abName);
        foreach (string rely in relys)
        {
            // 判断依赖包是否已加载
            if (!abDic.ContainsKey(rely))
            {
                ab = AssetBundle.LoadFromFile(PathUrl + rely);
                abDic.Add(rely, ab);
            }
        }
        // 加载资源包
        if (!abDic.ContainsKey(abName))
        {
            ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }
    }
    /// <summary>
    /// 异步加载资源,GameObject类型可直接帮助实例化
    /// </summary>
    /// <param name="abName">加载的资源包</param>
    /// <param name="resName">加载的资源名称</param>
    /// <param name="callBack"></param>
    public void LoadResAsync(string abName, string resName, UnityAction<Object> callBack)
    {
        StartCoroutine(LoadResAsyncCoroutine(abName, resName, callBack));
    }
    private IEnumerator LoadResAsyncCoroutine(string abName, string resName, UnityAction<Object> callBack)
    {
        LoadAB(abName);
        // 如果是GameObject,帮助实例化
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName);
        yield return abr;
        if (abr.asset == null)
        {
            Debug.LogError("异步加载失败!读取资源为空!");
        }
        if (abr.asset is GameObject)
            callBack(Instantiate(abr.asset));
        else
            callBack(abr.asset);
    }

其他代码也大同小异,无非就是重写官方的三种同步加载和异步加载的方法,使之先读取依赖包,顺便能帮助GameObject进行实例化。
具体代码可以查看【唐老狮】Unity热更新之AssetBundle

使用AB包管理器,就可以避免项目中混乱的包体关系,推荐指数五颗星。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/789242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式操作系统会不会是操作系统的终端形态?

昨天一位网友私信我&#xff0c;提出一个问题&#xff1a;“Laxcus分布式操作系统会不会是操作系统发展的终极形态&#xff1f;”。今天觉得有必要把这件事说一说&#xff0c;所以就忙里偷闲写下这篇文章。 咱们先说结论&#xff1a;是也不是&#xff0c;需要具体情况具…

shell 脚本通过 dumpsys SurfaceFlinger --latency 数据计算 FPS 和评价流畅度。

目录 前言&#xff1a; 开篇前述&#xff1a; 一、设计初衷 二、设定预期倒推查找解决方案 设计实现部分 一、确定数据来源原因&#xff08;dumpsys SurfaceFlinger --latency&#xff09; 二、根据需求确定计算规则 三、代码实现 四、监控数据可视化交互结果设计 前言…

uni-app个人中心

一. 介绍uni-app&#xff1a; uni-app 是基于Vue.js框架开发的一个跨平台移动应用开发框架&#xff0c;可以同时支持多个平台&#xff08;如iOS、Android、Web等&#xff09;的应用开发。采用了统一的语法和组件规范&#xff0c;可以大大简化跨平台开发的工作&#xff0c;提高…

Redis持久化 :rdb与aof的持久化操作

redis持久化&#xff1a;分别启用rdb和aof&#xff0c;并查看是否有对应文件生成 rdb&#xff1a; #save 秒钟 写操作次数 如果在设置时间内写入数据达到规定的次数&#xff0c;则产生一次快照 [rootlocalhost redis-stable]# vim /etc/redis.conf :/save #查找有save关键字的…

Oracle 多条记录根据某个字段获取相邻两条数据间的间隔天数,小于31天的记录都筛选出来

需求描述&#xff1a;在Oracle中 住院记录记录表为v_hospitalRecords&#xff0c;表中FIHDATE入院时间&#xff0c;FBIHID是住院号&#xff0c; 我想查询出每个患者在他们的所有住院记录中是否在一个月内再次入院(相邻的两条记录进行比较)&#xff0c;并且住院记录大于一的患者…

window10脚本转服务教程

先说下脚本/我们启动的一些三方服务转window本机服务目前我了解到的好处 一键设置开机自启、随用随启、延时自启解决一些服务类应用启动后会阻塞当前dos窗口导致桌面一直要开着的问题脚本化服务注册&#xff0c;方便管理&#xff0c;统一运维… 1. 实践涉及内容介绍 编写好的…

力扣刷题SQL-619. 只出现一次的最大数字

MyNumbers 表&#xff1a; ------------------- | Column Name | Type | ------------------- | num | int | ------------------- 这张表没有主键。可能包含重复数字。这张表的每一行都含有一个整数。 单一数字 是在 MyNumbers 表中只出现一次的数字。 请你编写一…

解决uview1.x使用i18n,props在切换语言的时候未及时修改视图的问题

操作流程&#xff0c;用u-modal举例 未修改的u-modal.vue props取消文案 props:{// 取消文案cancelText: {type: String,default: 取消}, } 在这里插入代码片需要修改成适配i18n的 u-modal.vue //跟着官方的this.$t(lang.intro)写法&#xff0c;不知道是我没引好还是怎么的&a…

TCP 协议【传输层协议】

文章目录 1. 简介1.1 TCP 协议是什么1.2 TCP 协议的作用1.3 什么是“面向连接” 2. 简述 TCP2.1 封装和解包2.2 TCP 报文格式2.3 什么是“面向字节流”2.4 通过 ACK 机制实现一定可靠性 3. 详述 TCP3.1 基本认识TCP 报头格式16 位源/目标端口号32 位序列号*32 位确认应答号4 位…

机器学习:GPT3

GPT3 模型过于巨大 GPT3是T5参数量的10倍&#xff01; 训练GPT3的代价是$12百万美元 Zero-shot Ability GPT3的思想是不是能拿掉Fine-tune 只需要给定few-shot或者zero-shot就能干相应的任务了。 few-shot learning&#xff08;no gradient descent&#xff09;&#…

25.3 matlab里面的10中优化方法介绍——Nelder-Mead法(matlab程序)

1.简述 fminsearch函数用来求解多维无约束的线性优化问题 用derivative-free的方法找到多变量无约束函数的最小值 语法 x fminsearch(fun,x0) x fminsearch(fun,x0,options) [x,fval] fminsearch(...) [x,fval,exitflag] fminsearch(...) [x,fval,exitflag,output] fmins…

基于MSP432P401R爬坡小车【2020年电赛C题】

文章目录 一、任务清单1. 硬件部分2. 软件部分 二、OpenMV巡线三、舵机转向四、停止线识别五、技术交流 一、任务清单 1. 硬件部分 主控板&#xff1a; MSP432P401R数据显示&#xff1a; OLED电机&#xff1a; 霍尔编码器电机电池&#xff1a; 7.3V航模电池巡线&#xff1a; …

网络知识点之-路由

路由&#xff08;routing&#xff09;是指分组从源到目的地时&#xff0c;决定端到端路径的网络范围的进程。路由工作在OSI参考模型第三层——网络层的数据包转发设备。路由器通过转发数据包来实现网络互连。虽然路由器可以支持多种协议&#xff08;如TCP/IP、IPX/SPX、AppleTa…

iptables与防火墙

目录 防火墙 安全技术 划分方式 iptables 构成 四表 优先级 五链 iptables的规则 匹配顺序 iptables的命令格式 管理选项 匹配条件 控制类型 隐藏扩展模块 注意事项 防火墙 隔离功能&#xff0c;一般部署在网络边缘或者主机边缘&#xff0c;在工作中防火墙的…

【【51单片机LCD1602模块介绍】】

LCD1602的介绍 显示容量16x2 每个字符是5x7的点阵 VDD 是电源正极 4.5-5.5v VO 是对比度调节电压 RS 数据/指令 选择 1为数据0为指令 RW 读写选择1是读 0为写 E 使能 1为数据有效 下降沿执行命令 D0-D7 数据输入输出 A 背光电源正极 K 背光电源负极 LCD1602的操作流程 1.初始…

艺人商务代言:避雷策略与成功合作之道

避免在艺人商务代言中遇到风险&#xff0c;是每个企业和艺人都应该高度重视的问题。代言活动是一种有效的市场营销手段&#xff0c;可以为企业带来广泛的曝光和销售增长&#xff0c;同时也能让艺人获得额外的收入和更高的知名度。然而&#xff0c;不慎选择错误的代言合作可能带…

PostgreSQL实战-数据库迁移部署

PostgreSQL实战-数据库迁移部署 介绍 根据项目需求&#xff0c;我们需要将现有的PostgreSQL数据库重新部署到新的服务器上。由于项目本身就是基于PostgreSQL数据库构建的&#xff0c;因此数据库迁移将变得十分便捷。接下来&#xff0c;我将简要介绍我们的迁移步骤。 迁移步骤…

IDEA安装热部署插件JRebel详解

JRebel 简介 JRebel是一套JavaEE开发工具。JRebel允许开发团队在有限的时间内完成更多的任务修正更多的问题&#xff0c;发布更高质量的软件产品。 JRebel是收费软件&#xff0c;用户可以在JRebel官方站点下载30天的评估版本。 Jrebel 可快速实现热部署&#xff0c;节省了大量重…

微服务的各种边界在架构演进中的作用

演进式架构 在微服务设计和实施的过程中&#xff0c;很多人认为&#xff1a;“将单体拆分成多少个微服务&#xff0c;是微服务的设计重点。”可事实真的是这样吗&#xff1f;其实并非如此&#xff01; Martin Fowler 在提出微服务时&#xff0c;他提到了微服务的一个重要特征—…

【Linux】Ubuntu基本使用与配置, 以及常见问题汇总(一)

前言 大学期间&#xff0c;感觉很多时候学习课外知识都是被推着往前走&#xff0c;很多内容并没有深入去学习&#xff0c;知识的记录受限于所学比较片面&#xff0c;如今渐渐意识到似乎并没有建立起相关知识的体系架构&#xff0c;缺乏一个系统学习并整理的过程。本文将以Ubunt…