关于Unity资源加载的细节

来源:互联网 发布:js给div添加点击事件 编辑:程序博客网 时间:2024/04/28 17:19

参考文章:http://www.cnblogs.com/88999660/archive/2013/03/15/2961663.html


Unity3D 里有两种动态加载机制:一个是Resources.Load,另外一个通过AssetBundle,其实两者区别不大。 

Resources.Load就是从一个缺省打进程序包里的AssetBundle里加载资源,而一般AssetBundle文件需要你自己创建,运行时 动态加载,可以指定路径和来源的。

其实场景里所有静态的对象也有这么一个加载过程,只是Unity3D后台替你自动完成了。


详细说一下细节概念:
一、AssetBundle
运行时加载:

1、 LoadFromFile:从指定本地磁盘路径加载,(注意这种方法只能用于standalone程序)这是最快的加载方法,(CreateFromFile方法在5.3.7版本被移除

1.1、LoadFromFileAsync: 异步文件加载,从返回的AssetBundleCreateRequest对象的assetBundle字段中得到

2、 LoadFromMemory(byte[]): 从内存中加载,这个byte[]可以来自文件读取的缓冲,www的下载或者其他可能的方式。( CreateFromMemory方法在5.3.7版本被移除

2.1、LoadFromMemoryAsync:异步内存加载,返回与LoadFromFileAsync相同。

其实WWWassetBundle就是内部数据读取完后自动创建了一个assetBundle而已

Create完以后,等于把硬盘或者网络的一个文件读到内存一个区域,这时候只是个AssetBundle内存镜像数据块,还没有Assets的概念。


二、Assets的加载:

1、 AssetBundle.LoadAsset(string name):从AssetBundle的内存镜像里读取并创建一个Asset对象,创建Asset对象同时也会分配相应内存用于存放(反序列化) (AssetBundle.Load的重命名方法

1.1、LoadAssetAsync(string name):异步加载,从返回的AssetBundleRequest对象的asset字段中得到

AssetBundle

2、LoadAssetWithSubAssets(string name):加载Asset及SubAssets,返回Object[] 或 T[] (5.0版本加入方法)

2.1、LoadAssetWithSubAssetsAsync(string name):异步加载Asset及SubAssets,返回AssetBundleRequest对象,从allAssets字段获取。

3、LoadAllAssets:加载全部Asset,可以指定Type,返回Object[] 或 T[] (AssetBundle.LoadAll的重命名方法)

3.1、 LoadAllAssetsAsync: 异步加载全部Asset,返回AssetBundleRequest对象,从allAssets字段获取。(5.0版本加入方法


三、AssetBundle和Assets的释放:

1、AssetBundle.Unload(flase)是释放AssetBundle文件的内存镜像,不包含Load创建的Asset内存对象。

2、AssetBundle.Unload(true)是释放那个AssetBundle文件内存镜像和并销毁所有用Load创建的Asset内存对象。

3、Resources.UnloadUnusedAssets: 释放所有没有被引用的Asset对象。

4、Resources.UnloadAsset(Object asset) :只会释放从本地加载的Asset对象。


四、Asset的Clone和使用:

当使用load好的Assets对象,要对其Clone一份并挂接到目标节点上,这是一个浅拷贝的过程,比如GameObject transform新生成的,而像 mesh texture material shader只是引用了源Assets对象。

专门要提一下的是一个特殊的东西:Script Asset,看起来很奇怪,Unity里每个Script都是一个封闭的Class定义而已,并没有写调用代码,光Class的定义脚本是不会工作的。其 实Unity引擎就是那个调用代码,Clone一个script asset等于new一个class实例,实例才会完成工作。把他挂到Unity主线程的调用链里去,Class实例里的OnUpdate OnStart等才会被执行。多个物体挂同一个脚本,其实就是在多个物体上挂了那个脚本类的多个实例而已,这样就好理解了。在new class这个过程中,数据区是复制的,代码区是共享的,算是一种特殊的复制+引用关系。

当你Destroy一个实例时,只是释放那些Clone对象,并不会释放引用对象和Clone的数据源对象,Destroy并不知道是否还有别的object在引用那些对象。

等到没有任何 游戏场景物体在用这些Assets以后,这些assets就成了没有引用的游离数据块了,是UnusedAssets了,这时候就可以通过 Resources.UnloadUnusedAssets来释放,Destroy不能完成这个任 务,AssetBundle.Unload(false)也不行,AssetBundle.Unload(true)可以但不安全,因为它不管Asset是否被引用,都会释放,所以不安全。


配个图加深理解:(图是老图,理解就行)



五、注意


1、AssetBundle文件的内存镜像,并不是在托管堆上,所以GC是不会代你自动释放,每次使用完,记得调用Unload手动释放。

2、如果不再使用某个Asset,请确保没有变量指向这个Asset对象,或者Clone出来的对象,哪怕这个Clone出来的对象已释放,UnloadUnusedAssets也不能释放这个Asset,除非这个变量本来被销毁或者设为null。

3、在面向移动设备游戏的制作时,尽量减少在Hierarchy对资源的直接引用,而是使用Resource.Load的方 法,在需要的时候从硬盘中读取资源,在使用后用Resource.UnloadAsset()和Resources.UnloadUnusedAssets()尽快将其卸载掉。降低内存占用

4、如果DontDestroyOnLoad了一个包含很多资源(比如大量贴图或者声音等大内存占用的东西)的话,这部分资源在场景切换时无法卸 载,将一直占用内存

5、DontDestroyOnLoad的脚本,而这些脚本很可能含有对其他物体的Component或者资源的引用,这样相关的 资源就都得不到释放。

6、static的单例(singleton)在场景切换时也不会被摧毁,同样地,如果这种单例含有大量的对资源的引用,也会成为大问题