英文网页地址https://unity3d.com/de/learn/tutorials/topics/best-practices/assets-objects-and-serialization
1.1首先要理解Assets和 UnityEngine.Object.是两种不同的事物
Assets是磁盘上的文件,比如纹理,材质,FBX文件。
UnityEngine.Objects是一个Unity引擎使用的一个数据对象,代表资源的实例。比如网格,sprite,动画剪辑。前述每一个资源都是一个内置类型,继承自UnityEngine.Object。还有用户自定义的资源,就是 ScriptableObject , MonoBehaviour 。MonoBehaviour 引用一个MonoScript。
1.2 对象间的引用
每个资源都可能引用到其它资源。序列化时,每个引用都由两部分构成,文件GUID和本地ID。
文件GUID是资产文件的唯一标识,存在.meta文件中,当一个资产文件导入引擎时生成。
本地ID是用来区分一个Assets中的多个Objects的标识。
1.3为什么有文件GUID和本地ID
文件GUID实现了资产文件和磁盘位置的无关性。这样无论资产文件如何移动,只要让它的meta和它一起移动,那么引用都不会错乱
unity在一次运行中维护了一个路径到文件GUID的map。该map的每一项的生成是Assets加载或导入时生成。 Editor没有关闭的情况下,如果.meta掉了而Assets文件本身没有挪动,那么unity会生成一个一模一样的.meta。
如果.meta掉了而Editor没有开着,或者Assets文件路径变化了,但没有一起移动相应的.meta文件,那么Editor将为Assets生成出不一样的文件GUID。引用就丢失了。
1.4组合的Assets和importers
非原生的资源必须通过importers导入引擎。这些importers都在导入时被自动调用。
导入处理后,就会生成一个或多个UnityEngine.Objects。在编辑器中就可以展开一个父资产看到许多子资产。比如一个Texture下面可以看到很多的sprite。
导入过程会转资源格式到目标平台,并且会进行纹理压缩等操作。
导入后的资源存到了Library目录下。按文件GUID的前两位区分的子目录 Library/metadata/ folder
1.5序列化和实例
虽然有文件GUID和本地ID的机制,但并不好用,比如要比较查找这些ID时并不高效。因此unity在运行时生成了实例ID,即Instance ID。Unity维护了一个从文件GUID和本地ID到Instance ID的映射缓存.当一个Object加载到内存后,它们之间的引用是通过Instance ID来表示的.通过Instance ID来得到一个资源对象时,如果它已经在内存中,那么就直接返回这个资源对象,如果不在内存中,那么再通过文件GUID和本地ID去磁盘加载.
开始时Instance ID的映射缓存只包含场景引用的资源对象,以及Resources目录下的 所有对象.运行时导入新资源或从AssetBundles加载了对象,都会相应增加Instance ID缓存的条目.
卸载AssetBundle 时会删除掉Instance ID缓存中对应的条目.再次加载AssetBundle 时,unity会为其中的资源生成新的InstanceID.
注意在iOS下应用切后台时,如果资源对象所属的AssetBundle 被卸载了,那么unity可能就无法恢复这些资源.因为iOS可能会释放掉这部分内存.
1.6MonoScripts
MonoBehaviour 引用一个MonoScript。
而MonoScript包含三部分信息:程序集,类名,名字空间.
1.7 资源生命周期
UnityEngine.Object有两种方式被加载:一是被间接引用到了.另一种是被API显示加载,比如AssetBundle.LoadAsset.
加载后,Unity会为把任何文件GUID和本地ID的引用翻译成以Instance ID表示的引用.
在下面两个情况都满足的条件下,一次对Instance ID的引用会导致对象加载动作产生:
1. Instance ID引用的对象目前没有被加载
2. Instance ID对应的文件GUID和本地ID是有效的.
在三种情况下对象会被卸载:
1. 用Application.LoadLevel 强切场景(非additive 模式).或调用Resources.UnloadUnusedAssets.此时只会对非引用的对象进行卸载,被脚本引用或被其它对象引用的对象不会被卸载.
2.调用 Resources.UnloadAsset API.此时Instance ID缓存数据还在.任何对对象的引用都会导致资源对象重新加载.
3. 调用 AssetBundle.Unload(true)API.这是对来自于AssetBundle的资源的强力卸载.因为它会删除Instance ID缓存数据
4. 调用 AssetBundle.Unload(false)API.unity对已经加载的资源对象不销毁,但是它会删除Instance ID缓存数据,因此如果这些对象不在内存后(比如iOS系统行为),unity不能根据引用自动地加载它们.只能重新加载AssetBundle.
1.8 加载复杂层次结构的GameObject
加载复杂层次结构的GameObject时,CPU时间消耗主要以下四点:
1.读磁盘
2.建立Transforms的父子关系
3.初始化GameObject和组件
4.调用Awake
生成一个GameObject有两种方式,要么读盘加载,要么直接从内存复制出一个对象.这两种方式来说,2,3,4点的消耗都一样.只有第1条的消耗不同.而如果一个GameObject的层次结构越复杂,它从磁盘加载的速度越慢.因此,要优化加载速度,可以简化GameObject的层次结构,然后对于重复的节点可以通过在内存中复制来生成.
在unity5.4中,生成一个GameObject时引擎会立即设置父节点.因此我们最好调用可设置父节点的GameObject.Instantiate重载函数来实例化,这可能会节约5~10%的时间