Contents

05Prefab相关

prefab相关

本文主要记录Unity中Prefab相关的一些细节。辅助理解Prefab这个在Unity中常用的对象究竟是什么。

UnityPrefab实际上可以理解为一种数据资源的整合结构。可以方便的将一些定义好的组合结构存储成起来。本文主要参考Unity官方论坛的一些资料以及实践。

Prefab格式说明

根据参考资料来。Prefab实际上一个储存为YAML格式的文件。YAML是一种可读的序列化格式,类似于XML或者JSON。Unity为了性能考虑,仅实现了YAML语法结构的一个特定子集。也就是说YAML有部分语法特性在Unity中是无效的。

这里我们主要关注的是Unity构建Prefab所定义的结构。对于一个空Gameobject所构成的Prefab。其内容如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4632272890024472620
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 6418570937168620578}
  m_Layer: 0
  m_Name: ChildGameObject
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!4 &6418570937168620578
Transform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 4632272890024472620}
  serializedVersion: 2
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
  m_LocalPosition: {x: 0, y: 415, z: 0}
  m_LocalScale: {x: 1, y: 1, z: 1}
  m_ConstrainProportionsScale: 0
  m_Children: []
  m_Father: {fileID: 0}
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

其中前两行为每个prefab文件都有的部分。在后面的数据中也不会出现重复。第一行表示所使用的YAML格式。第二行则是类似于创建了一个小的宏,定义!u!就是tag:unity3d.com,2011:

其后的部分则是包含在这个prefab中一系列对象的定义。每一个都会用一个显然的```来做区分。先看这个简单Gameobject的第一个Object对象。

每个Object都一定有头部两行。

  • 第一行则定义了Object的CLASS ID和FILE ID。其格式为— !u!{CLASS ID} &{FIELD ID}。例如图中CLASS ID为1而FILE ID为4632272890024472620。
  • 第二行则定义OBJECT TYPE。例如图中OBJECT TYPE为GameObject。
  • 下面其余部分则是这个Object中各个数据字段的序列化。对于每个字段名称,其都会以m_为开头。例如m_Name则说明了这个Prefab的name为ChildGameObject,m_TagString则说明了这个Prefab的Tag为Untagged,即没有tag标记。

实际上CLASS ID也指明了该Object对象属于Unity中的什么类型对象。具体的可以参见下表: https://docs.unity3d.com/Manual/ClassIDReference.html 这里的CLASS ID为1也说明这是一个Gameobject对象。

而FILE ID则是起到了引用标记的作用。当我们引用一个Prefab内的对象时,会通过将FIELD ID添加到对应字段内来实现引用关系。参看上面的Prefab格式数据,这个Gameobject有一个Component这个Component引用了一个Prefab内对象,对象的FILE ID为6418570937168620578。而这正是下面Transform的FILE ID。 而{fileID: 0}则表示这个字段为null没有引用任何对象。

值得说明的是,对于Unity中各种由Unity产出的资源实际都遵守这样一种数据格式。例如Scene,Animation等都是由相同的格式构成的,只是其中标识字段的语义不太一样。

Meta文件

上面可以看到一个Prefab文件是怎么初步建立文件的引用关系的。对于文件内的对象,会通过FILE ID来建立这一层引用。现在有个问题如何引用Prfab之外的对象呢。例如一个Image组件上的图片。而这个就是通过Unity对每个导入文件生成的Meta文件来实现。

例如一个带有SpriteRenderer组件的Prefab我们可以看到如下序列化结构。

即该Sprite字段引用了一个对象,这个对象的FIEL ID为21300000。但是实际上这个对象不在Prefab内,也找不到对应FILE ID的Object。其后跟了一个guid字段,2a191196225f2314aa62eeddbc3d5810,该字段则说明了该对象来自于一个全局对象。实际上就是一个Sprite贴图。其后还有一个type字段。type则用来确定资源来自于Assets目录还是Library目录。

  • Type 2:资源文件可以直接被Editor从Assets文件夹下加载。
  • Type 3:资源文件已经被处理和写入到Library文件夹中了。可以从Library中被Editor来加载。

所以对于Unity来说,每个文件到处后都附带一个meta文件是有必要的。另外还有两点是:

  • 对于Meta文件来说,对于某些文件类型,其还有很多的额外导入设定。这些也都会写在meta文件之中。例如对于一张贴图来说,其导入后的Texture设置,mipmap等信息也都在meta文件之中。
  • 对于Unity加载资源来说,实际上都是从Library中获取的。但源文件会在Assets目录之中。所以每次Unity都需要一个Import阶段,来处理Asset中资源生成Library中数据。

Prefab Instances

有些时候Prefab会存在在其他对象之中。例如场景Scene之中。这个时候实际上会在对应的文件中生成一个重要的Object对象来承担引用功能,即PrefabInstance对象。来看场景中引用一个简单的prefab对象的数据结构:

可以注意到m_SourcePrefab字段引用了一个全局的guid,这个就是Prefab实例所对应的Prefab资源。而FILE ID也是相同的一个占位地址。而m_Modification字段则标识了这个Prefab Intance的修改。可以看到

  • m_TransformParent:该对象在场景中的父对象
  • m_Modifications:在该对象上字段发生的修改变化。其中每个数据段会呈现出如下形式。
  • m_Removed:在Prefab基础上移除的Component与Gameobject。
  • m_Added:在Prefab基础上增添的Component与Gameobject

每一个都会有一个target来指明修改的对象。guid为目标prefab的全局guid,而fieldID则正是该对象在该prefab中的FILE ID标志。可以对照最前面的prefab数据格式,可以看到这里的两个对象就是前面的Gameobject以及对应的Transform。propertyPath则说明了修改数据字段的路径。value则说明了修改后的数值。

Nested Prefabs

现在我们就会看到一个问题。对于放在其他资源对象中的Prefab Instance实际上被没有一个FILE ID。那么在嵌套的Prefab中怎么建立引用关系呢。例如上面的ChildGameObject被放在另一个Prefab之中时。这个时候Unity实际上会生成一个占位的Object对象,并标记为 stripped。如下图:

对于Prefab内的引用会通过8665414460562454504,即这个Object的FILE ID来建立。而这个占位对象则有三个字段来指明

  • m_CorrespondingSourceObject:指明了这个对象的guid以及对象的fileid。
  • m_PrefabInstance:则指明了是哪个PrefabInstance对象。

参考资料

https://blog.unity.com/engine-platform/understanding-unitys-serialization-language-yaml