U3D资源加载

版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/blues1021。 https://blog.csdn.net/Blues1021/article/details/78167747
Unity3D 里有两种动态加载机制:一个是Resources.Load,另外一个通过AssetBundle,其实两者区别不大。 Resources.Load就是从一个缺省打进程序包里的AssetBundle里加载资源,而一般AssetBundle文件需要你自己创建,运行时 动态加载,可以指定路径和来源的。
开发时候用Resources.Load较简单(放在Resources目录下),主要看发布时候使用的AssetBundle(在StreamAssets目录下)及从服务器下载。
U3D用AssetBundle来动态下载和加载资源,资源包括纹理,模型,动画,声音,场景,文本TextAsset资源(lua数据)等。
各种asset可以打包为assetBundle,上传到服务器。
下载assetBundle并将包含的asset加载到游戏中使用。

一.资源制作过程:

1)BuildPipeline.BuildAssetBundle实现。

二.下载和加载过程:

下载分为缓存机制和非缓存机制:

非缓存机制下下载后的assetBundle不会进入unity引擎特定的缓存区。
非缓存机制:
var www: WWW = new WWW(url);
yield www;
缓存机制:
缓存机制下,会将下载过的assetBundle放置到特定的缓存区下。可以避免重复下载。
var ww: WWW = WWW.LoadFromCacheorDownload(url, 5);
yield www;
下载过程也会对www指向web stream文件存在内存结构,只有www = null或www.dispose()时候将www及www指向的web stream 内存结构卸载掉
其中下载下来的www文件数据,unity会用额外decompression buffer来解压存放,当解压出来后还会保留一个decompression buffer,其它的decompression buffer会被清理掉,目的是为了提高内存重用,提高性能。这层逻辑不用管,u3d引擎会自己维护。

加载过程:

1.加载assetBundle到内存

当把assetBundle文件从服务器端下载到本地后,需要将assetBundle加载到内存中并创建assetBundle文件内存对象。
有三种方式加载assetBundle:
1)www.assetBundle属性。
var ww: WWW = WWW.LoadFromCacheorDownload(url, 5);
yield www;
if(www.error != null)
{
return;
}
var myLoadAssetBundle = www.assetBundle; // 通过属性访问来加载一个assetBundle文件对象到内存中。
2)AssetBundle.CreateFromFile
支持从磁盘中创建一个assetBundle到内存中,这个方法仅支持非压缩格式的assetBundle,即创建assetBundle时必须使用UncompressedAssetBundle选项。
3)AssetBundle.CreateFromMemory
可以从内存数据流创建assetBundle内存对象,例如先用解密算法解密资源,然后通过该函数在内存中构建assetBundle内存对象。

2.从assetBundle内存中加载asset资源

asset-Object:原始资源
1)AssetBundle.Load通过名字将asset同步的加载到内存中。
AssetBundle.Load(name): 从AssetBundle读取一个指定名称的Asset并生成Asset内存对象,如果多次Load同名对象,除第一次外都只会返回已经生成的Asset 对象,也就是说多次Load一个Asset并不会生成多个副本(singleton)。
Resources.Load(path&name):同上,只是从默认的位置加载。
2)AssetBundle.LoadAsync通过名字将asset异步的加载到内存中。
AssetBundleRequest request = bundle.LoadAsync("myObject", typeof(GameObject));
yield return request;
GameObject obj = request.asset as GameObject;
3)AssetBundle.LoadAll 将所有assets加载到内存中。

gameobjec类型,clone-object拷贝asset
加载出来的asset,一般使用GameObject.Instantiate接口来实例化出GameObject供U3D直接使用,实例化时候纹理材质一般共享一份,gameObject的位置大小缩放有每个对象自己的数据
Instantiate(object):Clone 一个object的完整结构,包括其所有Component和子物体(详见官方文档),浅Copy,并不复制所有引用类型(mesh / texture / material / shader/terrainData/脚本代码   等),值类型transform/脚本实例和脚本数据等可以复制。有个特别用法,虽然很少这样 用,其实可以用Instantiate来完整的拷贝一个引用类型的Asset,比如Texture等,要拷贝的Texture必须类型设置为 Read/Write able。

拓展,动态生成游戏对象的方式:
一是静态引用,建一个public的变量,在Inspector里把prefab拉上去,用的时候instantiate
二是Resource.Load,Load以后instantiate
三是AssetBundle.Load,Load以后instantiate
三种方式有细 节差异,前两种方式,引用对象texture是在instantiate时加载,而assetBundle.Load会把perfab的全部assets 都加载,instantiate时只是生成Clone。所以前两种方式,除非你提前加载相关引用对象,否则第一次instantiate时会包含加载引用 assets的操作,导致第一次加载的lag。AssetBundle.Load是更好的方式,没有lag性能开销,但是也要load后instantiate出来asset使用。

非gameObject类型的资源不需要instantiate,但也有原始资源:
例如:
从AssetBundle1里读取并创建一个Texture Asset,把obj1的主贴图指向它
obj1.renderer.material.mainTexture = AssetBundle1.Load("wall") as Texture;
把obj2的主贴图也指向同一个Texture Asset
obj2.renderer.material.mainTexture =obj1.renderer.material.mainTexture;
释放时候要:
AssetBundle1.Unload(false);//那obj1和obj2不变,只是AssetBundle1的内存镜像释放了
Destroy(obj1);//obj1被释放,但并不会释放刚才Load的Texture
Destroy(obj2);//obj2被释放,但也不会释放刚才Load的Texture
Resources.UnloadUnusedAssets();//这时候刚才load的Texture Asset释放了,因为没有任何引用了

3.卸载assetBundle

(1) 卸载实例化 Instantiate 出来的asset使用:
GameObject.Destroy(), GameObject.DestroyImmediate()。释放掉clone-asset拷贝的非引用资源,对引用资源对象和对asset原始资源的引用计算减1。非gameObject资源,也会对它关联的资源引用计算减去1(如纹理)。
(2) 卸载asset原始资源
1)用assetBundle.Unload(true)卸载。
2)用 Resource.UnloadUnuseAssets() 来卸载。该接口会卸载掉没有被使用的asset(该原始asset的引用为0),作用效果不仅仅是assetBundle的,而是整个系统的(因为AssetBundle加载asset到游戏引擎中使用时候也归Resource管理),有较大耗性能。
Resources.UnloadAsset(Obj)释放指定的Asset对象资源,只能卸载磁盘文件加载的Asset对象 ,不能释放从assetBundle加载的。
(3) 卸载assetBundle内存对象
assetBundle.Unload(false); 只是将assetBundle内存对象卸载掉及减少web stream的引用计数,减少www引用计数,由它加载出来的asset并不受影响。
assetBundle.Unload(true);会将assetBundle内存对象,减少www引用计数,和由它加载出来的assets一起卸载掉。
(4)卸载www内存对象
www = null 或www.dispose()时候,web stream在assetBundle释放后,www释放后,引用计数为0,则GC会将
web stream内存结构卸载掉。
(5)如果要立即释放,则调用 GC.Collect()
否则内存未必会立即被释放,有时候可能导致内存占用过多而引发异常。
系统在加载新场景时,所有的内存对象都会被自动销毁,包括你用AssetBundle.Load加载的对象和Instaniate克隆的,因为这些是非托管的,需要用上述方式释放掉。
游戏进行的过程中尽量减少对GameObject的Instantiate()和Destroy()调用,提升CPU和内存性能,使用游戏对象池,对于暂时不需要的对象用scale = 0,移到屏幕外面,或setActive(false)即可。

猜你喜欢

转载自blog.csdn.net/Blues1021/article/details/78167747
今日推荐