◆◆◆◆
AssetBundle卸载
内存分析
在上图中的右侧,我们列出了各种内存物件的卸载方式:
● 场景物件(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的管理机制,敬请关注。