AssetBundle使用心得(转)

使用Assetbundle需要考虑的问题

Assetbundle资源打包时需要考虑如下几个问题:

1.如何有效地将依赖资源完整打包?如何减少资源重复打包,同时保证加载时不会导致内存超标?

2.如果多个资源打包在一个Assetbundle中,如何通过指定资源名查找定位到对应Assetbundle包名并加载?

3.创建多份相同资源(如副本中刷出多个相同的怪)GameObject时,想做到只加载一次Assetbundle和Prefab,提升加载效率,降低内存,如何设计缓存机制?

4.Assetbundle用完后如何释放?会不会有内存泄漏发生?如何避免?

资源依赖关系及打包规划

在Unity中一个Prefab文件可能会对另外的资源文件(如Prefab,贴图,shader等)有依赖,Unity提供了如下方式处理资源加载的依赖关系。

打包方法BuildPipeline.BuildAssetBundle(Object mainAsset, Object[] assets, string pathname, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform)

中参数assetBundleOptions使用BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets会将对象依赖的资源自动整体打入一个Assetbundle包中,这样保证了资源加载的完整性。

接着考虑到如果在打包时,将资源单独打包,那么多个资源所共用的资源就会被打包成多分,造成了冗余;如果将资源整体打成一个包,那么加载一个资源就需要加载整个Assetbundle包的镜像到内存,造成内存压力过大,对于资源量较大的游戏甚至发生游戏崩溃的情况。Unity提供方法

BuildPipeline.PushAssetDependencies()

BuildPipeline.PopAssetDependencies()

建立资源依赖关系,可以有效减小资源打包冗余。比如当打包shared, A, B三个资源时,伪代码如下:

BuildPipeline.PushAssetDependencies();

BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(“shared.prefab”), …);

BuildPipeline.PushAssetDependencies();

BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(“A.prefab”), …);

BuildPipeline.PopAssetDependencies();

BuildPipeline.PushAssetDependencies();

BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(“B.prefab”), …);

BuildPipeline.PopAssetDependencies();

BuildPipeline.PopAssetDependencies();

如果shared包含了A的资源,A就不会包含这个资源,而是依赖于shared中的;如果shared包含了B的资源,B也是依赖于shared中的;但是如果A包含了B的资源,B则不会依赖于A中的,而是自己包含这个资源。也就是说,在Push和Pop对中的资源可共享浅层级的资源,而无法共享层级更深的资源。

另外,在加载A资源之前,需要先加载shared资源,否则会出现A资源依赖的资源丢失问题。这个就要求开发者自己保证资源的加载顺序。对于资源量较大的实际工程,应当适度使用资源依赖,尽量避免资源加载时对加载顺序的限制,减小资源加载的复杂度,否则资源量大了之后较难管理维护。这里提供一些实际项目中的打包经验以供参考。

比如自定义的所有shader文件可以提取出来打包为公共Assetbundle包,避免出现每个依赖shader的资源都会单独打包shader,而造成冗余打包的情况。由于打包主要是为了支持资源级别的热更新,而shader一般情况下是不太可能被修改的,如果确定资源级别热更新不太可能更新shader,甚至可以将自定义shader文件放在Resources目录下不打Assetbundle包。

下来考虑模型资源,一般模型资源中最占空间的是材质贴图资源,可以要求美术将使用相同材质贴图的模型放在同一文件夹下,按照文件夹粒度打包,这样会有效地减小材质贴图重复打包造成的资源空间占用增长情况。

对于场景资源的加载一般也可按照场景的相似度,将共用大量物件贴图的场景放在一个文件夹中,将其打在一个Assetbundle包中,来减小场景资源空间增长的情况。

总之,在打包特定类型资源时需要按照这类资源依赖关系的特点,同时考虑减小Assetbundle包的粒度和减小依赖资源重复打包两方面使用Untiy提供的打包机制进行合理打包优化,达到加载到内存和资源包两方面冗余都能够满足游戏空间优化要求的目的。

Assetbundle资源命名管理

Unity在打包Assetbundle时没有提供现成的资源名到Assetbundle名映射关系。另外,Unity对于Assetbundle命名的要求是全局唯一。对于单个资源打包生成单个Assetbundle这种一对一的情况。可将Assetbundle名称命名为目录+文件名,以便于索引,这种情况一般用于本地数据表打包。但是对于资源共用比较多的场景和模型,一般会将多个贴图复用度较高的资源打包在一起,这就需要在创建Assetbundle包时建立资源文件名和Assetbundle包名的映射关系,将其序列化写入 xml文件中。游戏启动后先加载初始化资源映射关系表,以便于加载资源时通过资源名查找对应的Assetbundle包。

资源加载缓存机制

一份Assetbundle资源从加载到转化为GameObject需要经过如下三步:

(1)从网络或本地磁盘加载到内存生成Assetbundle文件内存镜像(异步);

(2)Assetbundle.Load接口加载内存镜像生成内存Prefab(阻塞式);

(3)Prefab被初始化复制生成GameObject(阻塞式);

如下图所示:

在移动平台下从网络或本地磁盘加载指定AssetBundle包到内存可以使用Unity提供的如下两个接口:

(1)       WWW bundle = new WWW(path);

(2)       WWW bundle = WWW.LoadFromCacheOrDownload(path, fileVer);

方法WWW.LoadFromCacheOrDownload会通过传入的下载地址和版本号在内存中查找是否已经有缓存的Assetbundle镜像,如果有,直接返回镜像;没有就会下载并缓存新的Assetbundle镜像。这里需要注意的是,传入的下载地址即使相同,版本号不同也会重新下载,对于没有加载多份相同镜像需求的常规情况,只需要将版本号参数设置为1 就好了。另外,缓存的释放机制由Unity自行管理,不需要使用者考虑,在磁盘空间不足的极端情况下,这个方法只是一个普通的new WWW。这是从Unity引擎层面保证相同的Assetbundle镜像在内存中只有一份,防止内存加载冗余。在实际项目中,开发者还需要自行管理加载缓存机制,不能只依赖于Unity的机制。在这里提供一种加载缓存的方法供大家参考讨论。

由上图可以看出Assetbundle加载后在内存里有Assetbundle镜像,未初始化的Prefab和初始化后复制的GameObject,设计缓存机制时需要从这三处考虑。缓存未初始化的Prefab既可以省略异步加载Assetbundle镜像的过程,又可以得到未修改的GameObject。因此首先定义一个以资源ID为Key值的Prefab字典。

Dictionary<string, GameObject>    mPrefabMap;

接着考虑加载一波相同怪物的情况。由于Assetbundle内存镜像的加载是异步的,因此无法按逻辑顺序判断避免重复加载Assetbundle。这里需要再定义一个以Assetbundle文件名为Key值的Assetbundle内存镜像字典。

Dictionary<string, Assetbundle>     mAssetMap;

相同资源再次加载时如果发现Assetbundle镜像正在加载中,缓存起来等待镜像加载完成处理。缓存结构如下:

Dictionary<string,List<ABLoader>> mLoadersMap;

这里Key为Assetbundle文件名,Value为待处理的加载器列表。

加载流程如下图所示:

在初始化GameObject后会立刻释放Assetbundle镜像和对应待加载的ABLoader对象列表。这种机制可以保证加载多份相同资源时只异步加载一次Assetbundle镜像(较为耗时),并且在内存中只缓存一份Prefab,再次加载相同资源时只需通过Prefab复制初始化即可。另外Prefab的释放时机可以选择一波怪GameObject销毁时,也可以选择关卡结束时,这个按照项目具体情况选择即可。

Assetbundle使用时需要注意的坑

Assetbundle在实际使用时遇到了很多的问题。这里总结一些典型的问题供大家参考,避免再次掉进坑里。

在打包Assetbundle时需要注意两个坑。一个是不做资源依赖打包的情况下不要添加Push和Pop对,否则会在部分机型(如华为P8)下出现加载资源积累到一定程度时(如加载几十波怪)出现资源贴图丢失的问题。另一个需要注意的是做资源依赖打包下面的写法是错误的。

BuildPipeline.PushAssetDependencies();

BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(“shared.prefab”), …);

BuildPipeline.PushAssetDependencies();

BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(“A.prefab”), …);

BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(“B.prefab”), …);

BuildPipeline.PopAssetDependencies();

BuildPipeline.PopAssetDependencies();

这样有可能会导致加载资源出现贴图丢失的情况。正确的写法如下:

BuildPipeline.PushAssetDependencies();

BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(“shared.prefab”), …);

BuildPipeline.PushAssetDependencies();

BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(“A.prefab”), …);

BuildPipeline.PopAssetDependencies();

BuildPipeline.PushAssetDependencies();

BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath(“B.prefab”), …);

BuildPipeline.PopAssetDependencies();

BuildPipeline.PopAssetDependencies();

每个依赖于Shared的资源打包时都需要用添加单独的Push和Pop对。

如果对自定义的Shader单独打Assetbundle包,需要在Edit->Project Settings->Graphics中添加自定义的shader引用,否则加载会出现Shader丢失的情况。

实际工程中为了减小DLL空间大小,通常会打开Stripping Level设置,剥离没有被使用的库。这样会出现以下情况,当模型骨骼都被做成Assetbundle包,并且模型使用了物理关节后加载模型崩溃,当场景被做成Assetbundle包,并且场景中使用了LightProbe,加载场景后,LightProbe影响的材质都会变黑。这些都是因为对应的物理库和LightProbe库没有被打包到安装包中,出现这种问题时可以在Assets目录下添加link.xml文件来手动加入需要引用的库。

另外,外网有部分玩家反馈,打了一段时间后无法进入场景。定位发现部分手机大文件清理会清除大于10M的文件,单个Assetbundle包大小不要超过10M。

以上是使用Assetbundle打包和加载资源的一些经验和心得,有需要改进的地方欢迎指正探讨。

                      转自:http://gad.qq.com/article/detail/7151362

时间: 2024-10-08 07:32:47

AssetBundle使用心得(转)的相关文章

Unity5新的AssetBundle系统使用心得

Unity的AssetBundle系统是对资源管理的一个扩展,动态更新,网页游戏,资源下载都是基于AssetBundle系统的.但是不得不说,这个系统非常恶心,坑很深.至于有多深,请看这篇文章: http://www.cnblogs.com/ybgame/p/3973177.html 原先的AssetBundle系统需要自己写一大坨导出的代码(BuildPipeline),这个新手会无从下手,老手也经常会被坑到.想正确处理好资源的依赖关系从而保证资源完整而又不会产生重复资源,确实不是一件非常容易

AssetBundle

再详细的介绍一下Unity5的AssetBundle http://liweizhaolili.blog.163.com/blog/static/16230744201541410275298/ Unity5的AssetBundle的一点使用心得 http://liweizhaolili.blog.163.com/blog/static/162307442015282017852/ 谈谈我用Unity5的AssetBundle踩到的几个坑 http://liweizhaolili.blog.16

Unity最新版打包AssetBundle和加载的方法

一.设置assetBundleName二.构建AssetBundle包三.上传AssetBundle到服务器四.把AssetBundle放到本地五.操作AssetBundle六.完整例子七.AssetBundle Manager管理工具八.备注知识 一.设置assetBundleName 如果没有设置AssetBundleName,会打包所有的Assets下的资源,如果设置,就只打包设置了名字的资源 1.在unity编辑器界面手动设置 输入所需的AssetBundle名称.请注意,AssetBu

Unity开发实战探讨-资源的加载释放最佳策略简要心得

Unity开发实战探讨-资源的加载释放最佳策略简要心得 看过我另外一篇关于Unity资源释放随笔<Unity开发实战探讨-资源的加载释放最佳策略>如果觉得略微复杂,那么下面是一些比较简要的心得体会: 概括 常用资源加载的方法有三种:静态,Resources内部资源,AssetBundle外部资源 资源释放的方式 有二种:立刻释放和统一释放. 静态 静态就是资源直接放场景,静态资源无法立刻释放,但场景关闭由引擎统一释放,开发者无法干预,所以最为无脑. 但静态过于死板,除了整个场景生命周期中必须使

Delphi组件indy 10中IdTCPServer修正及SSL使用心得

indy 10终于随着Delphi2005发布了,不过indy套件在我的印象中总是复杂并且BUG不断,说实话,不是看在他一整套组件的面子上,我还是喜欢VCL原生的Socket组件,简洁,清晰.Indy9发展到了indy10几乎完全不兼容,可叹啊.言归正传.在使用IdTCPServer组件的时候发现了他的漏洞,他的OnConnec,OnExecute,OnDisconnect等事件是在其他线程中执行的,通常情况下这没有问题,但是在特殊的情况下会造成问题,如果其他部分的程序写得有问题就会出现漏洞.

Linux系统理解以及Linux系统学习心得

原创作品转载请注明出处  <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 作者:严哲璟 说一下我对Linux系统的理解 1.加载Linux内核准备:在加载基本输入输出模块(BIOS)之后,从磁盘的引导扇区读入操作系统的代码文件块到内存中,之后开始整个系统的初始化. 2.main.c的start_kernel函数是整个操作系统的入口,这也与Linux是基于C语言的特性相符,start_kernel具体做的动作很多

Assetbundle 打包加载及服务器加载等(采用unity3d5.0后的新版)

Assetbundle为资源包不是资源 打包1:通过脚本指定打包 AssetBundleBuild ab = new AssetBundleBuild { assetBundleName = PlayerSettings.bundleVersion + "@" + "zhao",//资源包assets的名字 assetNames = new string[1],  //包里的每个资源的名字 }; string outputPath = Path.Combine(Ut

参加老男孩linux培训心得

参加老男孩linux培训心得 时间如白驹过隙,已经不知不觉来到老男孩培训已经三个月了.在这三个月中我渐渐得到了成长,专业技术成长,以及为人处事之道与思想的提高. 我一共就总结以下了三点     一.思想 在我刚来老男孩的时候,老师天天上课前讲一段思想,我当时不太明白,不赶紧上课,讲这干啥呢?而且老师早一点讲完,又可以招下一批学生了,老讲思想,这不是自断财路么?随着时间的流逝,渐渐的我悟懂了点.人在那里都可以学技术,但是学会了技术没有思想,一旦来了新事物,就会接受的很慢.尤其在互联网这个圈子里,软

Linux串口IO模式的一些心得

众所周知,在Linux系统下所有设备都是以文件的形式存在,串口也一样. 通常I/O操作都是有阻塞与非阻塞的两种方式. 其中"超时"这个概念其实是阻塞中的一种处理手段,本质还是属于阻塞的I/O模式. 在Linux中串口的IO操作 本文将它分为三种状态: 阻塞状态 超时状态 非阻塞状态 这三种状态的转换组合有这么几种: 阻塞 --> 超时 阻塞 --> 非阻塞 超时 --> 阻塞 超时 --> 非阻塞 非阻塞 --> 阻塞 我们一个一个来分析 首先在一个串口的