assets(Unity学习笔记 – Assets, Objects and Serialization)

Unity学习条记 – Assets, Objects and Serialization

Assets和Objects

Asset是存储在硬盘上的文件,保存在Unity项目标Assets文件夹内。好比:纹理贴图、材质和FBX都是Assets。一些Assets以Unity原生格式保存数据,比如材质。另一些Assets必要经过处理转换到原生格式,比如FBX。

Object是一系列序列化数据,这些数据形貌了具体的资源实例,这可以是Unity使用的随意典范的资源,比如mesh,sprite,audio clip或animation clip。一切的Objects都是UnityEngine.Object的子类。

大局部Object典范都是Unity内置的,但有两个特别典范:

1. ScriptableObject允许开发者界说他们本人的数据典范。这些典范可以由Unity序列化和反序列化,并且在编纂器的Inspector窗口中举行利用。

2. MonoBehaviour提供了链接到MonoScript的封装。MonoScript是Unity的内里数据典范,此中保存了指向在具体的步骤集和定名空间中的具体脚本类的引用。MonoScripte不包含任何实践可实行的代码。

Assets和Objects之间存在一对多的干系:也就是说,Asset文件内可以包含一个或多个Objects。

内里目标引用

一切的UnityEngine.Objects都可以引用其他的UnityEngine.Objects,被引用的Objects可以和引用的Objects位于同一个Asset文件内,也可以是由其他Asset文件导入的。比如,材质目标通常有一个或多个纹理目标的引用,这些纹理目标通常都是从纹理资源文件导入的(比如PNG或JPG)。

当序列化的时分,这些目标由两局部分散的数据构成:文件的GUID和Local ID。文件的GUID标志了存储资源的Asset文件。Local ID是局部唯一的(也就是说,在每个Asset文件中,Local ID都是唯一的),标志了Asset文件中的每个Object。

文件的GUID存储在.meta文件中。这些.meta文件是Unity第一次导入Assets时天生的,并且和Asset存储在同一个目次中。下图展现了Diffuse材质及其.meta文件:

.meta文件中包含了GUID:

掀开材质文件本身,可以看到Local ID:

假如在场景中有目标使用该材质举行渲染,那么掀开头景文件后,就会发觉该材质目标由GUID以及Local ID来标志:

为什么使用GUID和Local ID?

GUID的功效是提供文件途径的笼统表现。只需使用GUID来关联具体的文件,那么文件在磁盘上的地点就不关紧急了。因此可以随意挪动文件而不必要更新引用该文件的Objects(由于这些Objects存储的都是文件的GUID)。

由于一个Asset文件约莫包含多个UnityEngine.Object资源,因此必要用Local ID来明白的标志每个不同的Object。

假如一个Asset文件关联的GUID丧失的话,那么一切对该Asset文件中的Objects的引用都将丧失。当.meta文件丧失时,Unity会重重天生。

Unity维护了具体文件途径与GUID的映射干系。当一个Asset被加载或导入时,就会新增一个映射项,该映射项将Asset的文件途径和Asset文件的GUID毗连在一同。假如一个Asset的.meta文件丧失但其文件途径没有产生厘革的话,Unity能确保重重天生的.meta中纪录的GUID是坚持安定的。

假如.meta文件在Unity关闭时丧失,大概Asset文件的途径产生了厘革,但.meta文件没有随着一同挪动的话,那么一切对该Asset文件中的Objects的引用都将丧失。举个例子,场景中的Cube使用了我创建的材质Diffuse:

Diffuse材质及其.meta文件存储在Assets目次下,假如如今在外部挪动Diffuse材质到Assets/Temp目次下,由于没有同时挪动其.meta文件,因此Cube对其引用就会丧失:

资源及其导入

非Unity原生资源必需导入进Unity中才干使用,这是经过asset importer完成的。这些improter在资源导入时会被主动调用,同时你也可以用AssetImporter及其子类的API来经过代码调停资源导入历程。

资源导入的后果是一个或多个UnityEngine.Objects。在Unity中你可以看到一个父目标包含多个子目标,比如sprite atlas:。这些目标都共享同一个GUID,由于他们的源数据来自于同一个Asset文件。Unity使用Local ID来区分他们:

资源导入历程包含了十分耗时的利用,比如纹理紧缩。以是假如每次掀开Unity都必要实行一遍资源导入历程的话将会十分低效,因此,Unity将资源导入的后果缓存在Library文件夹中:。具体来说,存储在以Asset文件的GUID前两个数字定名的文件夹中,这些文件夹位于目次Library/metadata:

实践上即使是Unity原生资源,也会将导入后果存储在对应文件中。但是原生资源不必要很长的转换时间或重新序列化时间。

实例ID

只管GUID和Local ID健壮耐用,但是GUID的比力很耗时,而在运转时我们必要有个十分高效的体系。因此Unity在内里会维护一份缓存,这份缓存将GUID和Local ID转换成唯一无二的整数,这些整数被称为Instance ID,每当有新的Objects添加到缓存中时,Instance ID以简便的单调递增的办法举行赋值。缓存维护了Instance ID,GUID和Local ID(这两个界说了Object的源数据在磁盘上的地点)以及Object在内存中的实例(假如Object以前被加载到内存中的话)之间的映射干系。如此UnityEngine.Objects就可以维护互相之间的引用干系。经过Instance ID可以快速找到对应的以前加载的Object,假如对应的Object还没有加载,那么就可以经过GUID和Local ID来找到Object的源数据,然后加载相应的Object。

使用步骤启动时,项目内置目标(好比场景中使用的目标)的数据以及在Resources文件夹中的目标的数据将被初始化到Instance ID缓存中。当运转时有新的资源被导入(好比经过脚本创建的Texture2D目标),以及当从AssetBundle中加载目标时,就会在缓存中添加Instance ID项。Instance ID仅有在被以为以前过时的情况下才会从缓存中删除,这种情况产生在一个AssetBundle被卸载时。当一个AssetBundle被卸载时,除了会招致对应的Instance ID被以为以前过时,Instance ID和GUID以及Local ID之间的映射数据也会被从内存中删除。假如AssetBundle被重新加载的话,那么从该AssetBundle中加载的每一个目标都市创建一个新的Instance ID。

必要注意的是在具体平台上的一些特定事变会招致Objects从内存中被删除。好比当iOS上的使用步骤被挂起时,图形资源约莫会从显存中被删除,假如这些资源是来自一个以前被卸载的AssetBundle,那么Unity就无法重新加载这些资源了,任何对这些资源的引用也将变得没效(比如显现不偏见的模子(missing)使用粉色的材质(missing)来渲染)。

MonoScript

一个MonoBehaviour包含了一个对MonoScript的引用,而MonoScript仅仅包含了用于定位到一个具体脚本类所需的信息,他们都不包含脚本类的可实行代码。

一个MonoScript中包含了三个字符串:一个步骤集名,一个类名以及一个定名空间名。

当Unity构建项目时,会将Assets文件夹下的一切脚本文件编译到Mono步骤会合。具体来说,Unity会为在Assets文件夹中使用的每种不同的编程言语编译一个步骤集,并且会将在Assets/Plugins文件夹中的脚本单独编译到一个步骤会合。在Assets/Plugins文件夹外的C#脚本会被编译到Assetmbly-CSharp.dll中,在Assets/Plugins文件夹外的Java脚本会被编译到Assembly-UnityScript.dll中,Assets/Plugins中的脚本会被编译到
Assembly-CSharp-firstpass.dll中。

这些步骤集(再加上预编译的步骤集)都市被包含在终极的使用步骤中:

这些步骤集就是MonoScript引用的步骤集。和其他资源不同,一切步骤集在使用步骤第一次启动时会被全部加载过来。这种办法也是为什么一个AssetBundle(大概一个Scene、一个Prefab)中不包含挂载的MonoBehaviour组件中的可实行代码。这种办法使得不同的MonoBehaviour可以引用协同的具体类。

资源生命周期

有两种加载UnityEngine.Objects的办法:主动加载和体现的手动加载。当一个Instance ID被解引用,其对应的Object如今没有加载到内存中,并且Object的源数据可以被定位到时,Object会被主动加载。Objects还可以体现的在脚本中手动加载,比如新建一个Texture2D或经过AssetBundle.LoadAsset办法加载一个Object。

假如一个文件GUID和Local ID没有对应的Instance ID,大概一个Instance ID对应的Object没有被加载,并且其对应的GUID和Local ID是没效的话,那么Object就不会被加载,但是引用干系仍旧会被保存,此时在Unity编纂器中就会显现”(Missing)”。

Objects在底下三种具体的情况下会被卸载:

1. 当算账未被引用的Asset时,未被引用的Objects会被主动卸载。就地景切换时或当调用
Resources.UnloadUnusedAssets函数时会触发算账未被引用的Asset。

2. 来自Resources文件夹的Objects在调用Resources.UnloadAsset函数时会被烧毁。但是Instance ID会被保存,以是假如在Object被烧毁后,有任何先前对该目标的引用被解引用时,Unity会重新经过Instance ID找到GUID和Local ID,然后将该目标再次加载过来。

3. 来自AssetBundle的Objects在调用AssetBundle.Unload(true)函数时会被立刻烧毁,同时也会使得Instance ID,GUID和Local ID变得没效,任何对该目标的引用也会变成”(Missing)”。之后在C#中任何对该目标的拜候都市引发”NullReferenceException”特别。假如调用AssetBundle.Unload(false),从AssetBundle加载的Objects不会被烧毁,但是Instance ID对应的GUID和Local ID会变得没效,因此假如这些目标被从内存中开释的话,Unity将无法再次加载他们。

加载大层级目标

当序列化Unity GameObjects(比如Prefabs)时,要记取整个层级都市被序列化。也就是说,层级中每个GameObject及其组件在序列化数据中都市被独立的表现。因此,加载和实例化具有大层级的GameObjects时会有功能影响。

当实例化GameObjects时,实例化一个具有大层级的GameObject和实例化多个小层级的GameObjects然后将这些GameObjects组合在一同比拟,必要泯灭更多的CPU时间。只管实例化一个大层级的GameObject不必要组合GameObjects(不必要trampolining和SendTransformChanged回调)的CPU时间,但这些浪费的CPU时间远远比不外读取和反实例化大层级数据的时间。

之条件到,序列化GameObjects时,整个层级中的GameObject及其组件数据都市被序列化 — 即使这些数据是反复的。好比一个UI中有30个一样的Button,那么Button数据会被序列化30次。在加载时,这些数据都必要从磁盘上举行读取,在加载大层级的GameObjects时,文件读取时间会斲丧多量CPU时间。因此,可以把反复目标从整个层级中移出来,再单独实例化后再组合到整个层级中。

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发
火提提的头像-趣拿体育

昵称

取消
昵称表情代码图片