三星s7现在多少钱s7三星edge值得入手不

UIFramework之Unity4.x 资源加载策略
时间: 19:01:41
UIFramework之Unity4.x 资源加载策略
Unity4.x资源加载主要有从Resources文件夹下加载资源和从AssetBundle文件中加载资源这两种方式。
1、Resource资源加载
加载过程:源文件-&Asset-&Asset(Clone)
源文件到 Asset 的过程,主要的实现方式有:
①Resources.Load()同步加载Resource目录下的资源;
T ctrl = Resources.Load&T&(Path);
if (ctrl != null)
return Object.Instantiate(ctrl) as T;
}②Resources.LoadAsync()异步加载Resource目录下的资源;IEnumerator itor =LoadResourceAsyncIEnumerator&T&(Path,
//Action回调参数,匿名函数
if (null != go)
ctrl = Object.Instantiate(go) as T;
progress);
while (itor.MoveNext())
//Load Resource Async base function.
private IEnumerator LoadResourceAsyncIEnumerator&T&(string Path, Action&T& action, Action&float& progress)where T:Object
ResourceRequest req = Resources.LoadAsync&T&(Path);
while (req.progress & 0.99)
if (null != progress)
progress(req.progress);
while (!req.isDone)
if (null != progress)
progress(req.progress);
T ctrl = req.asset as T;
if (null != action)
action(ctrl);
}Asset 到 Asset(Clone)的过程主要是有 Instantiate()来实现的。
Asset(Clone)的卸载主要是通过 Destroy()来实现的。
Asset 的卸载一般是Reources.UnloadAsset(Object)和Resources.UnloadUnusedAssets()来卸载,Resources.UnloadUnusedAssets()一般这个函数我们不会手动调用,Unity 在切换场景时,会调用该函数,完成一些清理工作。
2、AssetBundle资源加载:AssetBundle 源文件-&AssetBundle 镜像-&Asset-&Asset(Clone)
AssetBundle 源文件到 AssetBundle 镜像:[由于AssetBundle打包存在依赖关系,故AssetBundle的加载需要根据依赖关系,加载被依赖的AssetBundle]
①AssetBundle.CreateFromFile:加载AssetBundle最快的方式。CreateFromFile只能适用于未压缩的AssetBundle,而Android系统下StreamingAssets是在压缩目录(.jar)中,因此需要先将未压缩的AssetBundle放到SD卡中才能对其使用CreateFromFile。iOS系统有256个开启文件的上限,通过CreateFromFile加载的AssetBundle对象也会低于该值。
②WWW.assetBundle:官方推荐使用 WWW + 协程的方式来异步加载 AssetBundle。
当 yield return www的时候,会将 www 对象抛给 unity3d 内核进行处理,虽然文档里没有太多内容,但是从接口提供的 WWW.isDone 和 WWW.progress 可以猜测 www 本质上是一个异步操作对象(AsyncOperation)。
即使是本地文件,即以 file:/// 协议开头的资源,也是可以使用上述的异步方式进行加载。
WWW.assetBundle在内存中会有较大的WebStream,而AssetBundle.CreateFromFile在内存中只有通常较小的SerializedFile。(对于序列化信息较多的Prefab,很可能出现SerializedFile比WebStream更大的情况)。
对于AssetBundle 镜像还未卸载的,如果重复加载,就会报错:
AssetBundle 镜像到 Asset:
①AssetBundle.Load():从AssetBundle读取一个指定名称的Asset并生成Asset内存对象,如果多次Load同名对象,除第一次外都只会返回已经生成的Asset 对象,也就是说多次Load一个Asset并不会生成多个副本(singleton)。
②AssetBundle.LoadAsync():异步读取;
③AssetBundle.LoadAll():全部读取;
AssetBundle.Unload(false):释放AssetBundle文件内存镜像(AssetBundle)。
AssetBundle.Unload(true):释放AssetBundle文件内存镜像(AssetBundle)同时销毁所有已经Load的Assets内存对象(Asset)。
Reources.UnloadAsset(Object):显式的释放已加载的Asset对象,只能卸载磁盘文件加载的Asset对象,CPU开销小。
Resources.UnloadUnusedAssets():用于释放所有没有引用的Asset对象,CPU开销大。
GC.Collect():强制垃圾收集器立即释放内存 Unity的GC功能不算好,没把握的时候就强制调用一下。
WWW对象:调用对象的Dispose函数或将其置为null即可;
WebStream:在卸载WWW对象以及对应的AssetBundle对象后,这部分内存即会被引擎自动卸载;
SerializedFile:卸载AssetBundle后,这部分内存会被引擎自动卸载;
在通过AssetBundle.Unload(false)卸载AssetBundle对象后,如果重新创建该对象并加载之前加载过的资源到内存时,会出现冗余,即两份相同的资源。
从AssetBundle读取一个指定名称的Asset并生成Asset内存对象,如果多次Load同名对象,除第一次外都只会返回已经生成的Asset 对象,也就是说多次Load一个Asset并不会生成多个副本(singleton)。多次Resource下Load,也只会返回第一次Load的Asset对象,也就是说,什么时候卸载AssetBundle镜像很关键,如果每次Instantiate()后,立即卸载AssetBundle镜像,当你再次读取AssetBundle后,就会Load出多余的Asset,从而出现资源冗余。
被脚本的静态变量引用的资源,在调用Resources.UnloadUnusedAssets时,并不会被卸载,在Profiler中能够看到其引用情况。
对于Prefab,目前仅能通过DestroyImmediate来卸载Asset,且卸载后,必须重新加载AssetBundle才能重新加载该Prefab。由于内存开销较小,通常不建议进行针对性地卸载。
尽量避免在游戏进行中调用Resources.UnloadUnusedAssets(),因为该接口开销较大,容易引起卡顿,可尝试使用Resources.Unload(obj)来逐个进行卸载,以保证游戏的流畅度。
比如需要从AssetBundle中加载A,然而A依赖于B,正确的顺序应该是先把A,B的AssetBundle加载进来,然后将A先Load为Asset,再实例化,在A实例化之前,不能Unload(false)。
Unity内存分析:
Unity游戏在运行时的内存占用
①库代码占用:Unity库,第三方库(减少打包时的引用库);
②本机堆(Native Heap):资源、Unity逻辑和第三方逻辑,Unity使用了自己的一套内存管理机制来使这块内存具有和托管堆类似的功能。尽量减少在Hierarchy对资源的直接引用,而是使用Resource.Load的方法,在需要的时候从硬盘中读取资源,在使用后用Resource.UnloadAsset()和Resources.UnloadUnusedAssets()尽快将其卸载掉。另外,static的单例(singleton)在场景切换时也不会被摧毁,同样地,如果这种单例含有大量的对资源的引用,也会成为大问题。
③托管堆(Managed Heap):C#代码(及时回收内存;使用对象池等,避免内存还未回收又申请新的内存)。
内存管理主要是针对托管堆(Managed Heap)内存:
目前绝大部分Unity游戏逻辑代码所使用的语言为C#,C#代码所占用的内存又称为mono内存,这是因为Unity是通过mono来跨平台解析并运行C#代码的,在Android系统上,游戏的lib目录下存在的libmono.so文件,就是mono在Android系统上的实现。C#代码通过mono解析执行,所需要的内存自然也是由mono来进行分配管理,下面就介绍一下mono的内存管理策略以及内存泄漏分析。
Mono通过垃圾回收机制(Garbage Collect,简称GC)对内存进行管理。Mono内存分为两部分,已用内存(used)和堆内存(heap),已用内存指的是mono实际需要使用的内存,堆内存指的是mono向操作系统申请的内存,两者的差值就是mono的空闲内存。当mono需要分配内存时,会先查看空闲内存是否足够,如果足够的话,直接在空闲内存中分配,否则mono会进行一次GC以释放更多的空闲内存,如果GC之后仍然没有足够的空闲内存,则mono会向操作系统申请内存,并扩充堆内存
GC的主要作用在于从已用内存中找出那些不再需要使用的内存,并进行释放。Mono中的GC主要有以下几个步骤:
1、停止所有需要mono内存分配的线程。
2、遍历所有已用内存,找到那些不再需要使用的内存,并进行标记。
3、释放被标记的内存到空闲内存。
4、重新开始被停止的线程。
除了空闲内存不足时mono会自动调用GC外,也可以在代码中调用GC.Collect()手动进行GC,但是GC本身是比较耗时的操作,而且由于GC会暂停那些需要mono内存分配的线程(C#代码创建的线程和主线程),因此无论是否在主线程中调用,GC都会导致游戏一定程度的卡顿,需要谨慎处理。另外,GC释放的内存只会留给mono使用,并不会交还给操作系统,因此mono堆内存是只增不减的。
一个Prefab从assetBundle里Load出来 里面可能包括:Gameobject transform mesh texture material shader script和各种其他Assets。
你Instaniate一个Prefab,是一个对Assets进行Clone(复制)+引用结合的过程,GameObject transform 是Clone是新生成的。其他mesh / texture / material / shader 等,这其中些是纯引用的关系的,包括:Texture和TerrainData,还有引用和复制同时存在的,包括:Mesh/material /PhysicMaterial。引用的Asset对象不会被复制,所以加载时不需要Instantiate(),只是一个简单的指针指向已经Load的Asset对象。这种含糊的引用加克隆的混合,
大概是搞糊涂大多数人的主要原因。
专门要提一下的是一个特殊的东西:Script Asset,看起来很奇怪,Unity里每个Script都是一个封闭的Class定义而已,并没有写调用代码,光Class的定义脚本是不会工作的。其实Unity引擎就是那个调用代码,Clone一个script asset等于new一个class实例,实例才会完成工作。把他挂到Unity主线程的调用链里去,Class实例里的OnUpdate OnStart等才会被执行。多个物体挂同一个脚本,其实就是在多个物体上挂了那个脚本类的多个实例而已,这样就好理解了。在new class这个过程中,数据区是复制的,代码区是共享的,算是一种特殊的复制+引用关系。
你可以再Instaniate一个同样的Prefab,还是这套mesh/texture/material/shader...,这时候会有新的GameObject等,但是不会创建新的引用对象比如Texture。所以你Load出来的Assets其实就是个数据源,用于生成新对象或者被引用,生成的过程可能是复制(clone)也可能是引用(指针)。当你Destroy一个实例时,只是释放那些Clone对象,并不会释放引用对象和Clone的数据源对象,Destroy并不知道是否还有别的object在引用那些对象。等到没有任何
游戏场景物体在用这些Assets以后,这些assets就成了没有引用的游离数据块了,是UnusedAssets了,这时候就可以通过Resources.UnloadUnusedAssets来释放,Destroy不能完成这个任 务,AssetBundle.Unload(false)也不行,AssetBundle.Unload(true)可以但不安全,除非你很清楚没有任何 对象在用这些Assets了。
为什么第一次Instaniate 一个Prefab的时候都会卡一下?因为在你第一次Instaniate之前,相应的Asset对象还没有被创建,要加载系统内置的 AssetBundle并创建Assets,第一次以后你虽然Destroy了,但Prefab的Assets对象都还在内存里,所以就很快了。
作者:husheng0 发表于 21:01:41 原文链接
阅读:30 评论:0 查看评论unity精通(29)
接上期的讲解,我们今天为大家继续探秘AssetBundle,从管理机制的角度出发,谈谈其资源加载和卸载的原理。
AssetBundle加载基础
通过AssetBundle加载资源,分为两步,第一步是获取AssetBundle对象,第二步是通过该对象加载需要的资源。而第一步又分为两种方式,下文中将结合常用的API进行详细地描述。
一、获取AssetBundle对象的常用API
(1)先获取WWW对象,再通过WWW.assetBundle获取AssetBundle对象:
public WWW(string url);
加载Bundle文件并获取WWW对象,完成后会在内存中创建较大的WebStream(解压后的内容,通常为原Bundle文件的4~5倍大小,纹理资源比例可能更大),因此后续的AssetBundle.Load可以直接在内存中进行。
public static WWW LoadFromCacheOrDownload(string url, int version, uint crc = 0);
加载Bundle文件并获取WWW对象,同时将解压形式的Bundle内容存入磁盘中作为缓存(如果该Bundle已在缓存中,则省去这一步),完成后只会在内存中创建较小的SerializedFile,而后续的AssetBundle.Load需要通过IO从磁盘中的缓存获取。
public AssetBundle assetB
通过之前两个接口获取WWW对象后,即可通过WWW.assetBundle获取AssetBundle对象。
(2) 直接获取AssetBundle:
public static AssetBundle CreateFromFile(string path);
通过未压缩的Bundle文件,同步创建AssetBundle对象,这是最快的创建方式。创建完成后只会在内存中创建较小的SerializedFile,而后续的AssetBundle.Load需要通过IO从磁盘中获取。
public static AssetBundleCreateRequest CreateFromMemory(byte[] binary);
通过Bundle的二进制数据,异步创建AssetBundle对象。完成后会在内存中创建较大的WebStream。调用时,Bundle的解压是异步进行的,因此对于未压缩的Bundle文件,该接口与CreateFromMemoryImmediate等价。
public static AssetBundle CreateFromMemoryImmediate(byte[] binary);
该接口是CreateFromMemory的同步版本。
注:5.3下分别改名为LoadFromFile,LoadFromMemory,LoadFromMemoryAsync并增加了LoadFromFileAsync,且机制也有一定的变化,可详见Unity官方文档。
二、从AssetBundle加载资源的常用API
public Object Load(string name, Type type);
通过给定的名字和资源类型,加载资源。加载时会自动加载其依赖的资源,即Load一个Prefab时,会自动Load其引用的Texture资源。
public Object[] LoadAll(Type type);
一次性加载Bundle中给定资源类型的所有资源。
public AssetBundleRequest LoadAsync(string name, Type type);
该接口是Load的异步版本。
注:5.x下分别改名为LoadAsset,LoadAllAssets,LoadAssetAsync,并增加了LoadAllAssetsAsync。
AssetBundle加载进阶
一、接口对比:new WWW与WWW.LoadFromCacheOrDownload
(1)前者的优势
后续的Load操作在内存中进行,相比后者的IO操作开销更小;
不形成缓存文件,而后者则需要额外的磁盘空间存放缓存;
能通过WWW.texture,WWW.bytes,WWW.audioClip等接口直接加载外部资源,而后者只能用于加载AssetBundle
(2)前者的劣势
每次加载都涉及到解压操作,而后者在第二次加载时就省去了解压的开销;
在内存中会有较大的WebStream,而后者在内存中只有通常较小的SerializedFile。(此项为一般情况,但并不绝对,对于序列化信息较多的Prefab,很可能出现SerializedFile比WebStream更大的情况)
二、内存分析
在管理AssetBundle时,了解其加载过程中对内存的影响意义重大。在上图中,我们在中间列出了AssetBundle加载资源后,内存中各类物件的分布图,在左侧则列出了每一类内存的产生所涉及到的加载API:
WWW对象:在第一步的方式1中产生,内存开销小;
WebStream:在使用new WWW或CreateFromMemory时产生,内存开销通常较大;
SerializedFile:在第一步中两种方式都会产生,内存开销通常较小;
AssetBundle对象:在第一步中两种方式都会产生,内存开销小;
资源(包括Prefab):在第二步中通过Load产生,根据资源类型,内存开销各有大小;
场景物件(GameObject):在第二步中通过Instantiate产生,内存开销通常较小。
在后续的章节中,我们还将针对该图中各类内存物件分析其卸载的方式,从而避免内存残留甚至泄露。
三、注意点
CreateFromFile只能适用于未压缩的AssetBundle,而Android系统下StreamingAssets是在压缩目录(.jar)中,因此需要先将未压缩的AssetBundle放到SD卡中才能对其使用CreateFromFile。
Application.streamingAsstsPath = &jar:file://& + Application.dataPath+&!/assets/&;
iOS系统有256个开启文件的上限,因此,内存中通过CreateFromFile或WWW.LoadFromCacheOrDownload加载的AssetBundle对象也会低于该值,在较新的版本中,如果LoadFromCacheOrDownload超过上限,则会自动改为new WWW的形式加载,而较早的版本中则会加载失败。
CreateFromFile和WWW.LoadFromCacheOrDownload的调用会增加RersistentManager.Remapper的大小,而PersistentManager负责维护资源的持久化存储,Remapper保存的是加载到内存的资源HeapID与源数据FileID的映射关系,它是一个Memory Pool,其行为类似Mono堆内存,只增不减,因此需要对这两个接口的使用做合理的规划。
对于存在依赖关系的Bundle包,在加载时主要注意顺序。举例来说,假设CanvasA在BundleA中,所依赖的AtlasB在BundleB中,为了确保资源正确引用,那么最晚创建BundleB的AssetBundle对象的时间点是在实例化CanvasA之前。即,创建BundleA的AssetBundle对象时、Load(“CanvasA”)时,BundleB的AssetBundle对象都可以不在内存中。
根据经验,建议AssetBundle文件的大小不超过1MB,因为在普遍情况下Bundle的加载时间与其大小并非呈线性关系,过大的Bundle可能引起较大的加载开销。
由于WWW对象的加载是异步的,因此逐个加载容易出现下图中CPU空闲的情况(选中帧处Vsync占了大部分),此时建议适当地同时加载多个对象,以增加CPU的使用率,同时加快加载的完成。
AssetBundle卸载
前文提到了通过AssetBundle加载资源时的内存分配情况,下面,我们结合常用的API来介绍如何将已分配的内存进行卸载,最终达到清空所有相关内存的目的。
一、内存分析
在上图中的右侧,我们列出了各种内存物件的卸载方式:
场景物件(GameObject):这类物件可通过Destroy函数进行卸载;
资源(包括Prefab):除了Prefab以外,资源文件可以通过三种方式来卸载:
1) 通过Resources.UnloadAsset卸载指定的资源,CPU开销小;
2)通过Resources.UnloadUnusedAssets一次性卸载所有未被引用的资源,CPU开销大;
3)通过AssetBundle.Unload(true)在卸载AssetBundle对象时,将加载出来的资源一起卸载。
而对于Prefab,目前仅能通过DestroyImmediate来卸载,且卸载后,必须重新加载AssetBundle才能重新加载该Prefab。由于内存开销较小,通常不建议进行针对性地卸载。
WWW对象:调用对象的Dispose函数或将其置为null即可;
WebStream:在卸载WWW对象以及对应的AssetBundle对象后,这部分内存即会被引擎自动卸载;
SerializedFile:卸载AssetBundle后,这部分内存会被引擎自动卸载;
AssetBundle对象:AssetBundle的卸载有两种方式:
1)通过AssetBundle.Unload(false),卸载AssetBundle对象时保留内存中已加载的资源;
2)通过AssetBundle.Unload(true),卸载AssetBundle对象时卸载内存中已加载的资源,由于该方法容易引起资源引用丢失,因此并不建议经常使用;
二、注意点
在通过AssetBundle.Unload(false)卸载AssetBundle对象后,如果重新创建该对象并加载之前加载过的资源到内存时,会出现冗余,即两份相同的资源。
被脚本的静态变量引用的资源,在调用Resources.UnloadUnusedAssets时,并不会被卸载,在Profiler中能够看到其引用情况。
UWA推荐方案
通过以上的讲解,相信您对AssetBundle的加载和卸载已经有了明确的了解。下面,我们将简单地做一下API选择上的推荐:
对于需要常驻内存的Bundle文件来说,优先考虑减小内存占用,因此对于存放非Prefab资源(特别是纹理)的Bundle文件,可以考虑使用WWW.LoadFromCacheOrDownload或AssetBundle.CreateFromFile加载,从而避免WebStream常驻内存;而对于存放较多Prefab资源的Bundle,则考虑使用new WWW加载,因为这类Bundle用WWW.LoadFromCacheOrDownload加载时产生的SerializedFile可能会比new WWW产生的WebStream更大。
对于加载完后即卸载的Bundle文件,则分两种情况:优先考虑速度(加载场景时)和优先考虑流畅度(游戏进行时)。
1)加载场景的情况下,需要注意的是避免WWW对象的逐个加载导致的CPU空闲,可以考虑使用加载速度较快的WWW.LoadFromCacheOrDownload或AssetBundle.CreateFromFile,但需要避免后续大量地进行Load资源的操作,引起IO开销(可以尝试直接LoadAll)。
2) 游戏进行的情况下,则需要避免使用同步操作引起卡顿,因此可以考虑使用new WWW配合AssetBundle.LoadAsync来进行平滑的资源加载,但需要注意的是,对于Shader、较大的Texture等资源,其初始化操作通常很耗时,容易引起卡顿,因此建议将这类资源在加载场景时进行预加载。
只在Bundle需要加密的情况下,考虑使用CreateFromMemory,因为该接口加载速度较慢。
尽量避免在游戏进行中调用Resources.UnloadUnusedAssets(),因为该接口开销较大,容易引起卡顿,可尝试使用Resources.Unload(obj)来逐个进行卸载,以保证游戏的流畅度。
需要说明的是,以上内存管理较适合于Unity 5.3之前的版本。Unity引擎在5.3中对AssetBundle的内存占用进行一定的调整,目前我们也在进一步的学习和研究中。
以上即为我们这次为您带来的AssetBundle管理机制,希望对您的项目研发有所帮助。我们会在后续技术文章通过大量的案例来进一步解释AssetBundle的管理机制,敬请关注。
&&相关文章推荐
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:30357次
排名:千里之外
原创:24篇
转载:117篇
(3)(1)(24)(26)(19)(1)(9)(6)(5)(2)(10)(3)(1)(2)(13)(13)(5)(2)(1)努力加载中,稍等...
暂无新消息
努力加载中,稍等...
已无更多消息...
这些人最近关注了你
努力加载中,稍等...
已无更多消息
努力加载中,稍等...
已无更多消息
Unity3D中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 | pleteAssets会将对象依赖的资源自动整体打入一个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&
mPrefabM接着考虑加载一波相同怪物的情况。由于Assetbundle内存镜像的加载是异步的,因此无法按逻辑顺序判断避免重复加载Assetbundle。这里需要再定义一个以Assetbundle文件名为Key值的Assetbundle内存镜像字典。Dictionary&string, Assetbundle&
mAssetM相同资源再次加载时如果发现Assetbundle镜像正在加载中,缓存起来等待镜像加载完成处理。缓存结构如下:Dictionary&string,List&ABLoader&& mLoadersM这里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打包和加载资源的一些经验和心得,有需要改进的地方欢迎指正探讨。
分类:(原创)Unity3D圈
登录后参与讨论。点击
请勿发表无意义的内容请勿发表重复内容请勿发表交易类内容禁止发表广告宣传贴请使用文明用语其它
淫秽色情政治倾向人身攻击抄袭剽窃广告刷屏恶意挖坟冒充他人其它

我要回帖

更多关于 s7三星edge值得入手不 的文章

 

随机推荐