Unity AssetBundle打包

news2024/12/23 8:35:42

1,AssetBundle的概念与作用


AssetBundle是一个存档文件,是Unity提供的一种用于存储资源的资源压缩包,可以包含模型、贴图、音频、预制体等。
Unity中的AssetBundle系统是对资源管理的一种扩展,通过将资源分布在不同的AB包中可以最大程度地减少运行时的内存压力,可以动态地加载和卸载AB包,继而有选择地加载内容。
而最重要的是AssetBundle可以用于热更新,是Unity更新非代码内容的主要工具。

2,AssetBundle组成


AssetBundle 主要由两部分组成:文件头和数据段

文件头包含了id、压缩类型、索引清单,该索引清单是与 Resources 相同的记录了序列化文件中的字节偏移量的查找表。对于大部分平台该表为平衡搜索树,对 Windows 和 OSX 系列(包括 iOS)则为红黑树,随着 AssetBundle 中对象的增加,构造清单所需时间的增长速度将超过线形增长速度
数据段包含了 Asset 经过序列化的原始数据,数据还可选择是否压缩,若使用 LZMA 压缩,则将所有 Asset 的字节数组整体压缩;若使用 LZ4 压缩,则将每个 Asset 单独压缩;若不压缩,则数据保持原始字节流

3,压缩模式

  • AssetBundle 提供了三种压缩格式
  • NoCompression:不压缩,解压快,包较大,不建议使用。
  • LZMA: 压缩最小,解压慢,用一个资源要解压包下所有资源。
  • LZ4: 压缩稍大,解压快,用什么解压什么,内存占用低,一般建议使用这种。


4,AssetBundle的构建


4.1 两种构建方式:

BuildPipeline.BuildAssetBundles(string outputPath, AssetBundleBuild[] builds, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform);
BuildPipeline.BuildAssetBundles(string outputPath, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform);


第1种方式打包时要手动收集所有AssetBundleBuild
第2种方式事先生成或手动填写所有需要打包的AssetBundleBuild


推荐第2种方式,可以在Unity编辑器看到所有要打包的AB,比较直观。

4.2 分包策略

  • 将需要同时加载的对象分为一组,如一个模型,其所需的材质和动画分为一组
  • 若多个 AssetBundle 中的多个对象引用了其他 AssetBundle 中的单个 Asset,则将依赖项分离到单独的包中以减少重复
  • 确保两组完全不可能同时加载的对象不在用一包中,如低清和高清材质包
  • 若一个包中只有低于一半的对象被频繁加载,可将其拆分
  • 将一些同时加载的小包(资源少于5到10个)合并

单个ab包多大合适?


一般控制在10MB以下,最好不要超过20MB,相比总大小,控制单个ab的资源数量尤为重要。如果项目中大部分ab都只包含单个资源,太过分散需要加载的ab过多,导致io压力增加;如果单个ab包含太多资源,首先热更新时容易增加下载资源量,也容易导致每次加载都有大部分用不到的资源,并且导致ab文件头复杂增加,解释时间也相对增加。所以要根据项目情况而具体分析,平衡才是最好的,通过经验一般单个ab包含10个左右资源为宜。

推荐的一种分包策略:


以每个文件夹为一个ab包,ab名就以文件夹路径为名,在这样的规则下,工程组织也可按文件夹分类,较为清晰。也可加入一些灵活的规则,如过滤一些不需要打包的文件后缀或文件夹,某文件夹的每个文件都为单独一个AB。

4.3 AssetBundle的参数(BuildAssetBundleOptions)


当我们调用Unity的API BuildPipeline.BuildAssetBundles去打AssetBundle的时候,实际上有很多的参数可以供我们选择。如果没有选择合适的参数,就可能会导致在包体,内存以及加载时间等方面造成很多的浪费。
实际上我们经常用到的有这么几个:

  • ChunkBasedCompression:这个参数是压缩AssetBundle的用的。前面提到Android的StreamingAssets是不压缩的。为了减小包体大小,可以使用该参数对AssetBundle进行压缩。它实际上是一个由Unity改良过的LZ4,使它的算法更符合Unity的使用方式。
  • DisableWriteTypetree:这个其实是会被很多开发者忽略的一个参数,它非常有用,可以帮我们减小AssetBundle包体的大小,同时也可以减小内存,以及减少我们加载这个AssetBundle时的CPU时间。
  • DisableLoadAssetByFileName,DisableLoadAssetByFileNameWithExtension:当我们加载好一个AssetBundle然后使用LoadAsset加载Asset的时候,需要传递Asset的路径名称。

4.4 自动构建流程

  • 构建前检查,如各种资源格式的检查,是否符合约定
  • lua脚本打包
  • 自动生成AssetBundle名字
  • 构建生成AssetBundle包
  • AssetBundle包加密
  • 生成AssetBundle md5
  • 删除旧ab资源
  • 拷贝首包ab资源到StreamingAssets
  • 生成各平台包,exe、apk、ios工程等


5,AssetBundle加载


5.1 加载方式


我们可以使用四种不同的方法来加载 AssetBundle。

  • AssetBundle.LoadFromMemory与AssetBundle.LoadFromMemoryAsync
  • AssetBundle.LoadFromFile与AssetBundle.LoadFromFileAsync(推荐)
  • WWW.LoadfromCacheOrDownload(5.6 及以前版本)
  • UnityWebRequestAssetBundle 和 DownloadHandlerAssetBundle(WebGL或需要网络加载的)

AssetBundle.LoadFromMemory
从内存区域创建一个AssetBundle,可以通过byte[]把AB包完整的加载出来。一般用于需要高度加密,或者WebGL,缺点:内存占用高,会占用两份内存。

AssetBundle.LoadFromFile
该方法可高效地从硬盘加载未压缩或 LZ4 压缩的 Assetbundle,加载 LZMA 压缩包时会先解压再加载到内存,加载 LZ4 它只会加载AB包的Header,之后需要什么资源再加载那部分的AB包chunk。从这可以看出使用 LZ4 构建AssetBundle的优势。

public class LoadFromFileExample : MonoBehaviour {
    function Start() {
        var myLoadedAssetBundle 
            = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle"));
        
        if (myLoadedAssetBundle == null) {
            Debug.Log("Failed to load AssetBundle!");
            return;
        }
        var prefab = myLoadedAssetBundle.LoadAsset.<GameObject>("MyObject");
        Instantiate(prefab);
    }
}


5.2 AssetBundle Asset 加载


AssetBundle加载完后,还不能直接使用,还需要从该ab中加载需要的Asset

  • AssetBundle.LoadAsset (LoadAssetAsync)
  • AssetBundle.LoadAllAssets (LoadAllAssetsAsync)

LoadAsset从ab包中加载指定的Asset,返回的是Object
LoadAllAssets从ab包中加载所有Asset,返回的是Object[],如在加载Sprite图集可以使用

5.3 同步与异步加载


不管是AssetBundle加载或者是AssetBundle里Asset的加载Unity都提供同步异步的加载方式,那么应如何选择呢?
实际上这只是一个策略的问题,并没有哪个更好。同步最大的优点是快,因为在这一帧里面主线程所有的CPU全都会归你用,所有的时间片全都归你用,它可以一门心思的把这件事情做完,再做其他的事情。但是同步的问题就是会造成主线程卡顿。异步可以简单的理解为多线程(其实还是有点区别的),最大的优点是不怎么会造成主线程的卡顿(也不是完全不卡顿),主线程可以尽量不卡顿的去跑。
也就是说卡顿不敏感的情况下可以使用同步,卡顿敏感的场景如战斗场景可以使用异步。
推荐做法:

  • 一般的UI界面,ui贴图,音效资源都可使用同步加载,通常不会感应卡顿,对ui逻辑处理也方便
  • 特效,角色模型,场景,背景音乐资源使用异步加载
  • 避免同步与异步同时进行,如一个ab里有多个Asset,用异步加载一个Asset时,同时又用同步加载另一个Asset,最好是不要把同步和异步加载的Asset放在同一个ab包里。
     

5.4 Editor加载资源与Resources加载


在Unity Editor下运行一般不使用AssetBundle加载资源,一方面是AssetBundle需要提前打包,另一方面是大部分shader显示有问题。
那么在Editor下一般用AssetDatabase.LoadMainAssetAtPath或AssetDatabase.LoadAllAssetsAtPath,对应于AssetBundle.LoadAsset和AssetBundle.LoadAllAssets,
AssetDatabase的加载需要知道资源路径,可通过AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName获得
AssetDatabase只有同步加载,那么对于AssetBundle异步加载,在Editor下最好模拟AssetDatabase的异步加载,如可以等待几帧。

Resources加载
Resources.Load只能加载Resources文件夹的资源,而Resources文件夹必须包含在首包,且不可热更,最重要的是启动时就自动加载,导致启动时间增加。因此,Resources.Load缺点非常明显,一般可用于首包必须要用到的资源加载,如配置资源。
 

6,AssetBundle依赖


使用AssetBundle一个非常有用的特性是AssetBundle的依赖,在Unity中当有资源需要复用时,可将该资源生成一个复用的ab包,这样在AssetBundle构建时其它引用该资源的ab包会自动关联依赖它。


那AssetBundle依赖对加载有什么影响呢?
当一个ab包有其它依赖包时,如果只加载该ab包,那么实例化的对象会出现资源丢失的现象。因此在ab加载Asset前必须加载它所有的依赖ab,注意是所有,要一直递归。
AssetBundleManifest.GetAllDependencies 可获取 AssetBundle 的所有依赖层级,manifest为主包


7,AssetBundle卸载


7.1 AssetBundle.Unload(bool)


参数分true和false,如果是true那就是把AssetBundle和它加载出来的Asset全都一起干掉。这个在不合适的时机就有可能发生资源丢失,出现粉色现象。如果是false,那么只是把AssetBundle给丢掉,Asset是不会被扔掉的。那么当你第二次去加载同一个AssetBundle的时候,在内存中就会有两份Asset,因为当AssetBundle被卸载的时候,它和对应的Asset的关系就被切割掉了。所以AssetBundle不知道之前的Asset是不是还在内存中,是不是从自己这加载出来的,容易导致内存泄漏。所以使用AssetBundle.Unload就很考验游戏的规划。


推荐使用AssetBundle.Unload(true),理由:程序应当自己管理维护AssetBundle,仅当引用AssetBundle的所有Asset都移除后才应该卸载该AssetBundle。

7.2 Resources.UnloadUnusedAssets


如果应用程序必须使用 AssetBundle.Unload(false),或者Resources.Load加载的Asset移除后,可使用Resources.UnloadUnusedAssets,它可以卸载掉那些没用的Asset,把它从内存中清除掉。Resources.UnloadUnusedAssets可能容易造成卡顿,需要注意调用时机,Unity在切换Scene的时候会自动调用一次UnloadUnusedAssets。

7.3 AssetBundle的管理与Asset的管理


要处理好AssetBundle的加载与卸载,少不了要有一套完善的管理系统。程序在实际使用时只关心Asset的获取,AssetBundle应做到完全透明,因此AssetBundle和Asset的管理要独立开。


推荐做法:

  • AssetBundle的管理AssetBundleManager放在C#层,Asset的管理AssetManager放在Lua层
  • AssetBundleManager和AssetManager分别都有自己的一套引用计数算法
  • 获取Asset:通过AssetManager获取Asset,Asset引用计数+1;AssetManager向AssetBundleManager获取Asset,AssetBundleManager计算Asset所属AB,加载对应AB,再通过AB加载对应Asset,AB引用计数+1
  • 释放Asset:通过AssetManager释放Asset,Asset引用计数-1;当该Asset引用计数为0时,可在适当时机通知AssetBundleManager释放Asset,AssetBundleManager计算Asset所属AB,AB引用计数-1,当AB引用计数为0,可在适应时机卸载AB
  • Asset通常只需要一份,如:贴图、音频、字体,文本、动画,AssetManager只需要向AssetBundleManager获取一次,每次使用引用计数+1即可,需要注意每个AssetBundle可能有多个Asset,引用计数应是其总和。另外Prefab是比较常用的一种Asset,它也只需要一份Asset,但每次使用需要实例化GameObject.Instantiate(asset),为避免经常实例化,最好使用对象池管理GameObject。
  • 在内存不紧张的情况下,可在场景切换时进行统一释放Asset,卸载AssetBundle

    

8,AssetBundle加密


Unity的AssetBundle非常容易破解,轻易就可获得原始资源,如AssetStudio工具,因此成熟的项目都需要考虑AssetBundle的加密。一种比较直接又可以自定义的加密方式是使用AssetBundle.LoadFromMemory(Async)

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    byte[] MyDecription(byte[] binary)
    {
        byte[] decrypted = new byte[1024];
        return decrypted;
    }

    IEnumerator Start()
    {
        var uwr = UnityWebRequest.Get("http://myserver/myBundle.unity3d");
        yield return uwr.SendWebRequest();
        byte[] decryptedBytes = MyDecription(uwr.downloadHandler.data);
        AssetBundle.LoadFromMemory(decryptedBytes);
    }
}

但AssetBundle.LoadFromMemory(Async)的使用成本非常高昂,不但内存增加,解密也需要耗时,一般不推荐使用。


推荐做法:
AssetBundle LoadFromFile(string path, uint crc, ulong offset);
offset参数可以自定义,是指AssetBundle内容的偏移量,只要在AssetBundle构建后

foreach (var abName in manifest.GetAllAssetBundles())
{
	string filePath = outputPath + abName;
	int offset = Utility.GetAssetBundlesOffset(abName);
	var fileContent = File.ReadAllBytes(filePath);
	int filelen = offset + fileContent.Length;
	byte[] buffer = new byte[filelen];
	fileContent.CopyTo(buffer, offset);

	FileStream fs = File.OpenWrite(filePath);
	fs.Write(buffer, 0, filelen);
	fs.Close();
}

Utility.GetAssetBundlesOffset 是自定义offset的算法,这里是根据ab名字来计算,也可以根据其它参数来计算,例如hashcode

基于offset加载AssetBundle:

public AssetBundle LoadAssetBundle(string abName)
{
	string path = abPath + abName;
	int offset = Utility.GetAssetBundlesOffset(abName);
	AssetBundle ab = AssetBundle.LoadFromFile(path, 0, (ulong)offset);
	return ab;
}

这样就达到了加密的效果,只要不知道offset的算法,就无法破解了,并且这种做法只是做了一下偏移,基本不会增加性能消耗。

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

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

相关文章

SpringBoot--Web开发篇:含enjoy模板引擎整合,SpringBoot整合springMVC;及上传文件至七牛云;restFul

SpringBoot的Web开发 官网学习&#xff1a; 进入spring官网 --> projects --> SpringBoot --> LEARN --> Reference Doc. --> Web --> 就能看到上述页面 静态资源映射规则 官方文档 总结&#xff1a; 只要是静态资源&#xff0c;放在类路径下&#xff1…

制作网页版H5页面商城源码系统+随心DIY 带前后端完整搭建教程

随着智能手机的广泛普及&#xff0c;人们越来越依赖手机进行日常生活中的各种活动&#xff0c;包括购物。传统的PC端购物模式已经无法满足人们的需求&#xff0c;因此开发移动端的购物系统势在必行。而现如今H5技术不断发展成熟&#xff0c;使得在手机等移动设备上展示网页版商…

Nginx常见问题解决

一、修改nginx.conf报错 背景&#xff1a;修改nginx.conf&#xff0c;配置转发到tcp的信息&#xff1a; 在stream块中配置转发规则&#xff1a;在stream块中&#xff0c;使用server指令来配置转发规则。例如&#xff0c;如果你要将TCP流量转发到example.com:1234&#xff0c;可…

短视频矩阵营销系统工具如何助力商家企业获客?

1.批量剪辑技术研发 做的数学建模算法&#xff0c;数学阶乘的组合乘组形式&#xff0c;采用两套查重机制&#xff0c;一套针对素材进行查重抽帧素材&#xff0c;一套针对成片进行抽帧素材打分制度查重&#xff0c;自动滤重计入打分。 2.账号矩阵分发开发 多平台&#xff0c;…

[学习笔记]python绘制图中图(绘制站点分布图)

背景 在绘制站点分布图时&#xff0c;有时需要采用图中图的方式&#xff0c;以便于在一张图中尽可能多的表达信息。此处记录一下利用python matplotlib绘制图中图的脚本&#xff0c;方便然后查询。 包含数据 该绘图脚本中包含以下数据&#xff1a; CMONOC站点分布&#xff…

日本移动支付Merpay QA团队的自动化现状

Merpay是日本最大的网购平台之一Mercari的无现金支付系统。Merpay 的主要功能是让用户在 Mercari的网站上购物&#xff0c;也可以在日本的许多实体店和餐厅使用它&#xff0c;也可以理解为日本的“支付宝”。以下为Merpay QA 团队在自动化方面的一些思考&#xff1a; 这几年&am…

C++构建与编译

C构建 一般来讲&#xff0c;写完c的源文件&#xff08;src&#xff09;&#xff0c;就需要去编译为&#xff1a; 可执行文件动态库/静态库 那么就遇到了几个问题&#xff1a; 编译的主机是什么代码运行的目标平台是什么 主机 一般来讲工作的机器&#xff0c;Windows或者L…

vMix导播软件使用NDI协议输入输出

前两篇文章一直写NDI&#xff0c;写了NDI在OBS【链接】、芯象【链接】中的使用。有的朋友问在vMix中的用法&#xff0c;今天就详细说说。 vMix在软件导播领域可算大名鼎鼎&#xff0c;功能丰富、工作稳定&#xff0c;支持多种媒体导入。由它衍生出多种产品&#xff0c;如&#…

中考倒计时7个月复习攻略:名师支招,为“长跑”积蓄能量

今天是2023年11月2日&#xff0c;距离2024年中考还有7个月多一点的时间。其实据六分成长了解&#xff0c;很多学校到了初三&#xff08;九年级&#xff09;基本上都是奔着中考考试复习的&#xff0c;这一学年基本上是长跑、拉力赛&#xff0c;拼的不只是智力&#xff0c;还有体…

chatgpt接口调用

在线接口文档&#xff1a; https://app.apifox.com/invite?tokensymrLP7sojF6N31kZqnpZ 接口地址 https://chat.xutongbao.top/api/light/chat/createChatCompletion 请求方式 POST 请求参数 token String, 必须 prompt Array, 必须 例子一&#xff1a; 包含上下文 [ { "…

LVGL库入门 01 - 样式

一、LVGL样式概述 1、创建样式 在 LVGL 中&#xff0c;样式都是以对象的方式存在&#xff0c;一个对象可以描述一种样式。每个控件都可以独立添加样式&#xff0c;创建的样式之间互不影响。 可以使用 lv_style_t 类型创建一个样式并初始化&#xff1a; static lv_style_t s…

二分法解决局部最小值问题

二分法解决局部最小值问题 局部最小值问题&#xff1a;(不一定是有序才可以二分的&#xff0c;具有排它性就可以二分) 局部最小就是改数的左侧和右侧的数都比自己大&#xff0c;当然如果位于两端的话&#xff0c;就只考虑一侧。找到有一个这样的数就可以。 一个数组中&#…

推荐游戏《塞尔达传说:旷野之息》

塞尔达传说&#xff1a;旷野之息 播报编辑讨论32上传视频 2017年任天堂企划制作本部开发的动作冒险游戏 3分钟了解荒野之息 03:59 一分钟了解游戏《塞尔达传说&#xff1a; 旷野之息2》 00:57 旷野之息&#xff1a;20-爬雪山找隐藏神庙获攀爬套装部件&#xff0c;踏上沼泽再…

Pytest-Allure及Allure命令使用

一、Allure介绍 Allure是Pytest用于生成测试报告的框架&#xff0c;提供丰富的测试报告功能&#xff1b; 二、Allure安装 Allure安装分为2块&#xff0c;分别是pytest-Allure库安装&#xff0c;本地生成报告并导出的命令行allure安装&#xff1b; 1、pytest-Allure库安装 …

易基因:RRBS等揭示DNA甲基化-肿瘤免疫逃逸-肾上腺皮质癌侵袭的相关性|表观研究

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 组蛋白修饰对调控染色质结构和基因表达至关重要&#xff0c;组蛋白修饰失调可能导致疾病状态和癌症。染色质结合蛋白BRWD3&#xff08;Bromodomain and WD repeat-containing protein 3&…

创新驱动发展 国家创新型城市试点名单2006-2018年(已整理DID格式)

数据简介&#xff1a;创新型城市是指在经济、科技和社会方面具有卓越创新能力的城市&#xff0c;这些城市通过提供良好的创新环境和支持系统&#xff0c;吸引和培养创新人才&#xff0c;促进新兴产业发展&#xff0c;推动经济增长和社会进步。创新型城市提供了丰富的就业机会和…

go语言 | grpc原理介绍(一)

参考 https://www.nowcoder.com/discuss/389810396381683712?sourceSSRsearch 这里是b站对应的csdn博客&#xff0c;比较详细的介绍grpc相关原理说明&#xff0c;首先是大概的一个流程图说明。 什么是 RPC &#xff1f; 远程过程调用&#xff08;RPC&#xff09;是计算机科…

嵌入式软件开发:第二部分–七个步骤计划

使用一种工具&#xff08;仅一种工具&#xff09;武装自己&#xff0c;您可以在下一个嵌入式项目的质量和交付时间上做出巨大的改进。点击领取嵌入式物联网学习路线 该工具是&#xff1a;绝对承诺对开发代码的方式进行一些小而基本的更改 。 有了改变的意志&#xff0c;今天您…

火山引擎ByteHouse:如何用OLAP引擎提升数字营销效果?

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 随着市场竞争的加剧&#xff0c;企业对数字营销投入的效果监测和优化需求日益增强&#xff0c;营销实时监控也成为企业提升运营效率的重要手段。在数字化营销中&…

Django实战项目-学习任务系统-查询列表分页显示

接着上期代码框架&#xff0c;6个主要功能基本实现&#xff0c;剩下的就是细节点的完善优化了。 接着优化查询列表分页显示功能&#xff0c;有很多菜单功能都有查询列表显示页面情况&#xff0c;如果数据量多&#xff0c;不分页显示的话&#xff0c;页面展示效果就不太好。 本…