Unity 打AssetBundle和加载方案
一、如何组织assetBundle:
unity5以前,打包需要自己去找依赖,然后需要按照拓扑图顺序压入AB栈,这样在最后打AB时才能有效利用依赖(栈内已有的AB才能作为依赖)。
unity5.x后,打包变得简单,但如何组织assetBundle依然需要琢磨和规划。
首先我们需要知道:AB、asset和资源的关系:一个AB包括1个或多个asset,一个asset可能没有依赖其他asset(即包括了其需要的所有资源),也可能依赖其他asset(包括一些资源和指向其他asset所在AB的引用)。
其次,使用asset和AB:要使用一个asset,需要先加载该asset的所有依赖的asset(迭代下去,把所有相关的AB都加载出来),然后加载自己的AB,最后取出asset来使用。
所以,要使用一个asset,都需要加载所有相关的AssetBundle镜像进入内存。这就需要我们要研究打ab的方案了。并且打ab的方式也影响ab的总大小,即使在Unity5下。
文档提供了3种打ab方案:
1.逻辑实体分类法,即根据功能模块分类,(大体上意思是不同功能不同ab,这样就可以开一个功能下载一个ab)比如先大层次分:UI模块,角色模块,场景模块等,
UI模块又根据具体功能模块分:比如各个副本的UI是不同的,那每个副本的UI可以打成单独ab;
角色模块则是每个角色包括其依赖的东西,如mesh,model,animation,texture等打成一个AB;
场景模块可以一个场景分成多个片,每个片相当于一个“子模块”,单独成ab,多个场景共享片。
2.根据资源类型打ab:比如音效打成一个ab,国际化文本打成1个ab等,文档说这种方法是打多平台ab的一种好方法,比如音效ab在各个平台下都一样则可以复用,而shader平台区分则不能复用
并且在此方法下,因为原始资源(比如Texture)很少改动,所以利于版本更新(增量少)。这种方法我也不是很理解,感觉就是把ab粒度细化到原始资源层次(纹理,音效,网格等),他们有的在不同平台保持一样,有的平台区分,但基本不会发生改变。
3.根据“同时加载”来打ab,即把需要同时加载的资源打入同一个ab.比如每个场景包括其依赖的所有东西打成一个AB。
无论使用什么方案(一般是都使用一点点,比如第3种用来打场景,第1种用来打各个系统(即功能)的资源,等),为了减少AB大小,增量AB大小,加载时间和运行时内存消耗,都需要注意下面细节:
1.频繁变动的asset不应该和稳定的asset打入一个AssetBundle;小增量AB
2.如果稳定的大/多asset中含有频繁变动的资源,应该把资源单独打AB;小增量AB
3.使用时一般同时加载的东西如Model和它的texture,material,animation等应该打入一个AssetBundle中,所以角色一般每个一个AB;减少加载时间
4.如果多个AB都依赖某个asset或资源,应该把该asset或资源单独打AB;小AB,省内存
5.如果2个asset极少同时加载,那应该分开打AB;省内存
6.把经常同时加载的小asset的AB合并成大AB;减少加载时间
二、打AB的参数选择和加载AB的方案:
1.清单:
1)打AB时,每个AB都会额外带一个.manifest文件,用来描述该AB相关信息,包括
CRC码,(file hash)a single hash for all assets in AB,(type tree hash)a single hash for all types in AB,(class types)all class types in AB,(asset names)all asset paths in AB.
此文件只用于生成增量AB, not necessary for runtime,所以不需要打入安装包,依赖关系可以在2)那里统一查找。
2)并且在打AB的输入参数中的“输出文件夹”那里生成一个.manifest文件,里面包括所有AB路径及其依赖,这个在运行时加载资源时使用,因为加载资源需先加载依赖。
2.AB的压缩方式
AB有3种压缩方式(当然你也可以自定义压缩算法,不过没必要),一种是无压缩UncompressedAssetBundle,一种是块压缩LZ4(ChunkBasedCompression),一种是最大压缩LZMA(None)
加载时无压缩最快,其次是块压缩(只需要解压AB的一部分就能加载,与无压缩具有可比性),最后是LZMA(需要完全解压AB),当然打出的AB大小就反过来了(LZMA:50-60%,LZ4:70%)。
一般本地AB建议以LZ4格式存储,较小且快,需要网络下载的用LZMA并使用LoadFromCacheOrDownload对其加载,这样到了本地就变成LZ4了(LoadFromCacheOrDownload在接受数据流时就直接解压,无额外耗时)。
3.AB加载的几个接口
1)加载AB文件(这里是指build出来后的AB文件,如果build后还进行加密等则不能直接加载)有几种方式:
WWW:输入URL,AB在www.assetBundle里;PS:如果ab在本地,url用file:///前缀
WWW.LoadFromCacheOrDownload:输入URL,AB在www.assetBundle里;PS:如果ab在本地,url用file:///前缀
LoadFromMemory(Async):输入是bytes[],返回的就是AB;PS:bytes的获取一般用WWW,也可以直接File.ReadAllBytes,反正获得文件的字节流即可。
LoadFromFile(Async):输入是path,AB在AssetBundleCreateRequest.assetBundle里;
UnityWebRequest:文档说此方法比WWW好,用法也类似,但因为WWW比较成熟,而且WWW我们基本只是用来加载本地bytes资源,而且此方法在5.4以后版本才有,我们没有使用。
在网络资源下载方面我们项目用的是C#提供的HttpWebRequest
2)加载非.assetBundle文件例如.bytes,.jpg,.png,.txt,.ogg,.mp3等文件一般使用WWW加载:
WWW.bytes,WWW.texture,WWW.text,WWW.audioClip里分别存加载后的数据。
如果原先是.assetBundle,加密成.bytes,先www加出bytes,然后loadfrommemory;
如果原先是.bytes或者其他自定义数据类型,如xml,json等,打ab成.assetBundle,则先加出assetBundle,然后加载出Asset,最后强转为TextAsset类型,然后你的数据就可以从中取出来了,Unity经常把这些数据看成TextAsset类型;
简而言之,对特殊文件格式,我们使用之前先测试一下其加载方式,基本在WWW和TextAsset这块考虑,看数据在它们哪个变量里,一测便知。
4.针对2和3介绍的多种方式,按情况选择压缩方式+加载接口
文档根据各个函数对内存和CPU消耗的特点做出以下建议:
1.对于安装包内的AB,尽量用LoadFromFile(Async)+ChunkBasedCompression,因为它和ChunkBasedCompression配合起来很快(可以压缩30%+而且加载时不需要额外解压),如果AB的压缩格式是LZMA(即Option=None),LoadFromFile(Async)需要额外解压和重压缩(压为LZ4);
2.对于网络资源,尽量用WWW.LoadFromCacheOrDownload,并且资源尽量用默认的LZMA压缩法,因为这样可以省流量,并且此函数在接收网络流时就对流进行解压,为LZ4(即AB压不压对其无区别),而加载LZ4不需要额外解压。
3.对于加密的AB,需要用WWW把文件加载成bytes,然后解密,然后用LoadFromMemory[Async]加载出AB(这个大概是LoadFromMemory[Async]使用的唯一场景了)
4.如果使用自定义压缩算法,选UncompressedAssetBundle不压缩来build AB,然后加载时先对数据解压,然后LoadFromFile(Async)
总结:本人当前项目是用LZMA build AB(不使用LZ4的原因可能是没深入了解,也可能是贪图LZMA的小包体,也可能是加载的多半是小ab,LZMA解压再压LZ4的消耗可以忍受),用LoadFromFile(Async)加载.assetbundle,WWW+LoadFromMemory(Async)加载加密过的ab.bytes,用WWW加载原始图片。
三、如果管理AssetBundle
1.AssetBundle.unload(true/false)的用法:
true会把AB,由AB加出来的Asset,由Asset实例化出来的GameObj(也可能只是指向Asset的引用)都销毁,内存一干二净;
false只是把AB卸载,官方文档描述为把AB和Asset,Gobj的link断开,即后者们自己删除自己,如果再次加载Asset则是新的Asset,与原来的Asset独立共存于内存;
这里需要明确两点:
1.AssetBundle加载出来后没有Unload则其镜像一直在内存,再次加载会报错,可以unload掉重新加载或者hold住它,不重新加载;
2.false时要卸掉Asset需要把由它实例化出来的Gobj都销毁(引用则置空),然后Resource.unloadunusedassets卸载,Gobj则destroy即可;
官方文档建议选择true,true调用的时机可以是切地图或者Asset引用为0时,如果选择false,那Asset只有在2的情况下才能卸掉。
但官方文档没说的一点是在调用unload(true)前如果再次加载应该怎么办。
1.其实只需在上层存个Pool即可,Pool里的元素对应一个AssetBundle,加载AB时先看Pool里有没有,有则直接使用它来GetAsset和实例化,并把引用+1,当实例化的东西Destroy时把该AB的引用减一,当引用变为0时unload(true)即可。
2.但这种方式主要用来处理实例化的GameObject资源,对某些资源比如AudioClip,Text,场景,自定义数据等一般很少频繁加载,这样我们在加载AB出来后直接unload(false),然后当它不使用时,对应的Asset引用就空了,就可以在UnloadUnusedAsset时卸载(切场景时默认调用一次,一般在程序中定时调用一次)
3.官方文档之所以建议使用unload(true),是想避免内存存多份Asset和Asset不能及时卸载,如果资源是很少加载如2中那些,用unload(false)较好,至于unloadunsedAsset的调用可以在该资源使用完后手动调用一下(当资源消耗时刻明确时),也可以交给游戏卸载系统(定时调用unloadunusedasset)或切场景。
我的一种方案:
A.对不频繁加载的资源(大部分都是),我们加载完后就unload(false),这样可以迅速释放内存镜像,而当加载出来的资源不再被使用(引用为空)时,Asset就能被识别为unsedassets而被卸载;
B.对频繁加载的资源(小部分),我们在上层弄一个pool,里面存着stringtoasset的字典,字典里只存asset,即即使是频繁加载的资源我们也要在加载出asset后用unload(false)把内存镜像卸载掉,只保留加载出来的资源以便实例化/引用,这里我们需要搞个引用计数,因为字典里永远引用着该资源,当引用计数为0时我们需要从字典中删除。
在A,B的指导下+定时调用Resources.UnLoadUnUsedAssets,GC.Collect应该能保持内存的尽量干净了。那哪些资源是频繁加载哪些是不频繁加载,标准是咋样的呢?这个需要具体测试得到相关标准和性能的关系才能做决定,和游戏逻辑有关,和资源有关,和体验有关,需要写工具来确定。
总结:AssetBundle的管理主要抓住几个接口即可AssetBundle.unload(true/false),Resources.UnLoadUnUsedAssets,GC.Collect,Destroy,但要真正了解他们,需要深入理解Unity对AssetBundle,Asset,资源等的内存管理机制,可以参考这篇文章:
http://www.cnblogs.com/88999660/archive/2013/03/15/2961663.html
四、Ps(存疑与补充)
AssetBundle Variant:ab变种,一般用于解决“多态性”问题,比如资源有hd,ld,图片要适应各种设备需要不同压缩格式等,有空可以研究一下。
Unity 打AssetBundle和加载方案的更多相关文章
- Unity最新版打包AssetBundle和加载的方法
一.设置assetBundleName二.构建AssetBundle包三.上传AssetBundle到服务器四.把AssetBundle放到本地五.操作AssetBundle六.完整例子七.Asset ...
- Demo示例——Bundle打包和加载
Unity游戏里面的场景.模型.图片等资源,是如何管理和加载的? 这就是本文要讲的资源管理方式--bundle打包和加载. 图片 Unity游戏资源管理有很多方式: (1)简单游戏比如demo,可以直 ...
- 【Unity游戏开发】SpriteAtlas与AssetBundle最佳食用方案
一.简介 在Unity步入2019.4以后,新版的SpriteAtlas日趋完善,已经完全可以在商业项目中使用了.但是纵观网络平台上,许多关于SpriteAtlas的文章还停留在2018的初版时期,其 ...
- AssetBundle压缩/内部结构/下载和加载
一.AssetBundle的压缩方式 Unity支持三种AssetBundle打包的压缩方式:LZMA, LZ4, 以及不压缩. 1.LZMA压缩方式 是一种默认的压缩形式,这种标准压缩格 ...
- Unity的资源加载以及AssetBundle的一些坑
https://www.cnblogs.com/sigmadruid/p/4040803.html AssetBundle加载完毕,进行其中Asset的初始化后,不能立即Unload().否则Asse ...
- Unity中 动态加载 Resources.Load()和Asset Bundle 的区别
版权声明:本文为博主原创文章,未经博主允许不得转载. 初学Unity的过程中,会发现打包发布程序后,unity会自动将场景需要引用到的资源打包到安装包里,没有到的不会跟进去.我们在编辑器里看到的Ass ...
- Unity 全面理解加载和内存管理
最近一直在和这些内容纠缠,把心得和大家共享一下: Unity里有两种动态加载机制:一是Resources.Load,一是通过AssetBundle,其实两者本质上我理解没有什么区别.Resources ...
- linux 编译,链接和加载
1. 序 最近在折腾各种.so,碰到了一些问题,一开始对于很多错误也没有头绪,茫然不知所措.索性化了一天多时间将<<程序员的自我修养—链接.装载与库>>中部分内容略读了一遍 ...
- Unity进阶----AssetBundle_02(加载依赖关系及网络资源)(2018/10/31)
网络资源加载: string path ="file://"+ Application.streamingAssetsPath + "\\windows\\123&quo ...
随机推荐
- PHP+Redis链表解决高并发下商品超卖问题
目录 实现原理 实现步骤 上一篇文章聊了一下使用Redis事务来解决高并发商品超卖问题,今天我们来聊一下使用Redis链表来解决高并发商品超卖问题. 实现原理 使用redis链表来做,因为pop操作是 ...
- Android 布局的一些控件的补充和布局的补充(今儿没课)
前面写的博客可能会有点乱: 1,是不太会排版. 2,就是我一边看书,一边听学长讲课,所以有的知识就融入进去了,我写的都是自己的意见和理解,大家取我精华,弃我糟粕哈. 今天是书上的内容,主要讲布局的,一 ...
- Java高级篇 JVM
JVM是什么? JVM起了什么作用? JVM包含了什么? JVM中, 一个类 程序是怎么加载的? JVM中垃圾回收机制?
- java中threadlocal的理解
[TOC] #java中threadlocal的理解##一.threadlocal的生命周期和ThreadLocalMap的生命周期可以吧TreadLocal看做是一个map来使用,只不过这个map是 ...
- Socket 由浅入深,开发一个真正的通信应用
在说socket之前.我们先了解下相关的网络知识: 端口 在Internet上有很多这样的主机,这些主机一般运行了多个服务软件,同时提供几种服务.每种服务都打开一个Socket,并绑定到一个端口上, ...
- C#算法设计排序篇之11-二叉树排序(附带动画演示程序)
二叉树排序(Binary Tree Sort) 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/695 访问. 二叉树排序 ...
- C#LeetCode刷题之#389-找不同(Find the Difference)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4062 访问. 给定两个字符串 s 和 t,它们只包含小写字母. ...
- RNN以及LSTM简介
转载地址 https://blog.csdn.net/zhaojc1995/article/details/80572098 本文部分参考和摘录了以下文章,在此由衷感谢以下作者的分享! https:/ ...
- 【HAOI2015】树上染色 - 树形 DP
题目描述 有一棵点数为 N 的树,树边有边权.给你一个在 0~ N 之内的正整数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的N-K个点染成白色 . 将所有点染色后,你会获得黑点两两之 ...
- PAT 2-08. 用扑克牌计算24点(25):
题目链接:http://www.patest.cn/contests/ds/2-08 解题思路:思路参考24点游戏技巧http://www.24game.com.cn/articles/points2 ...