游戏对象还没有在Runtime初始化的时候就有了数据,数据需要加载。在C++里面,类是定义,没有数据。所以游戏引擎就是要加一层数据,数据序列化/反序列化就是保存/加载的过程
外链资料:
关于这个东西是个啥,参考后面的序列化示例
完整的表可以参考官方的文档:
https://docs.unity3d.com/Manual/ClassIDReference.html
大概是这样一个东西:
ID | Class |
---|---|
0 | Object |
1 | GameObject |
2 | Component |
3 | LevelGameManager |
4 | Transform |
5 | TimeManager |
6 | GlobalGameManager |
8 | Behaviour |
9 | GameManager |
… | … |
Assets路径下的文件和文件夹都会有自己对应的meta文件,由Unity生成。
Meta文件除了保存资源GUID以外,还可能保存了某些修改,比如图集的切分信息,比如以下是某个png文件的meta(部分)。因为Unity不会修改外部资源,但是对外部资源的修改需要存储,所以就存在Meta里面,Meta和Unity原生文件格式一样,也是YAML
# 这是一个Meta文件的部分内容 ... spriteSheet: serializedVersion: 2 sprites: - serializedVersion: 2 name: pb_empty rect: serializedVersion: 2 # 记录了图片切分的方式 x: 59 y: 126 width: 1 height: 1 alignment: 9 pivot: {x: 20, y: -19} border: {x: 0, y: 0, z: 0, w: 0} outline: [] physicsShape: [] tessellationDetail: -1 bones: [] spriteID: 7303c9062e3178050800000000000000 internalID: 5802628507894558775 vertices: [] indices: edges: [] weights: [] ...
将Meta文件删除后,Unity会重新生成Meta文件,并且GUID和原来保持一致,这是如何做到的?在任何情况下都是这样吗?答:在资源路径没有变化的情况下,才能保证生成的GUID和原来一致
我们在代码中写一个public GameObject go;然后就可以在编辑器中选择资源了,但是你可能会发现,选择框分为“Scene”和“Asset”。在某些情况下,可能还没有这两个选项,只有一个选项。以前我总是认为,Scene内的是GameObject,Scene外部的是Asset,总是以为这两者之间是Runtime和Editor的区别,但其实Scene内的对象应该也算“资源”,因为Scene是资源,Scene的内容也就应该是资源。其实都是Unity Object。[我的理解]两个的区别应该就是Scene内部和外部吧,到时候序列化有没有GUID的区别吧。
# 这是一个Unity的场景文件,场景内容参考上面的截图 # 接下来我会用注释的方式解释一个这个文件的内容,看Unity是以怎样的形式序列化内容的 # Prefab和场景类似,这个场景中也包含了Prefab,可以参考一下Prefab在场景中存在的形式 # 这个文件中我会插入很多空行,也会删除一些行 # 文档的开头 %YAML 1.1 %TAG !u! tag:unity3d.com,2011: # ---表示一个"对象"的开始,这里要注意的是,这里说的"对象"不是程序中的对象,这里的"对象"是指以"---"开头的一段记录 # "对象"可能是GameObject(ID是1)也可能是Component或者PrefabInstance(ID是1001) # 然后是关于"!u!29 &1": # !u!29中,29表示:这个"对象"的种类ID,比如GameObject的类型ID是1 # 这个类型表可以参考:https://docs.unity3d.com/Manual/ClassIDReference.html # &1中,1表示:fileID,这里OcclusionCullingSettings比较特殊,fileID是个1,其他的fileID可能是很长的数字 --- !u!29 &1 # 然后,这顶级缩进的一行,表明这里记录的是什么内容,就是这个"对象"是个啥 OcclusionCullingSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_OcclusionBakeSettings: smallestOccluder: 5 smallestHole: 0.25 backfaceThreshold: 100 m_SceneGUID: 00000000000000000000000000000000 m_OcclusionCullingData: {fileID: 0} # 每个场景中都会有这些固定的内容,环境光、渲染设置等,相当于是场景的设置 --- !u!104 &2 RenderSettings: m_ObjectHideFlags: 0 ... --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 ... --- !u!196 &4 NavMeshSettings: serializedVersion: 2 ... # 这是第一个GameObject,名字是:Directional Light --- !u!1 &705507993 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 # m_Component记录了这个GameObject上的组件,使用fileID的形式引用到本文件内的"对象" m_Component: - component: {fileID: 705507995} - component: {fileID: 705507994} m_Layer: 0 m_Name: Directional Light m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 # 这是第一个Component,类型是Light # Unity原生的很多Component都是有自己单独的类型的,比如Light,类型ID是108 --- !u!108 &705507994 Light: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} # 引用了自己所属的GameObject m_GameObject: {fileID: 705507993} m_Enabled: 1 serializedVersion: 10 ... # Transform组件 --- !u!4 &705507995 Transform: m_ObjectHideFlags: 0 ... --- !u!1 &963194225 GameObject: m_ObjectHideFlags: 0 ... --- !u!81 &963194226 AudioListener: m_ObjectHideFlags: 0 ... --- !u!20 &963194227 Camera: m_ObjectHideFlags: 0 ... --- !u!4 &963194228 Transform: m_ObjectHideFlags: 0 ... # 这是第一个PrefabInstance类型 # Prefab实例在场景内就是以这样的形式存在的 # 当Unpack之后再观察,就是普通的GameObject了 # 或者将原本的普通GameObject保存为Prefab,可以再观察一下变化 # PrefabInstance也是有fileID的,因为和一个普通的Unity Object一样 # 另外说一下,Prefab引用本Prefab内的Object,只使用fileID,引用外部的Object,要带一个Guid --- !u!1001 &1253179838 PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 # 记录的是增量的修改 m_Modification: m_TransformParent: {fileID: 0} m_Modifications: # 以下就是各条的修改 # 记录目标的Unity Object,还是通过{}引用的方式 # 记录propertyPath和value - target: {fileID: 6189974477040685128, guid: 5b1e9ea1ec6b21649a535663d2128dc9, type: 3} propertyPath: m_RootOrder value: 2 objectReference: {fileID: 0} - target: {fileID: 6189974477040685128, guid: 5b1e9ea1ec6b21649a535663d2128dc9, type: 3} propertyPath: m_LocalPosition.x value: 0 objectReference: {fileID: 0} - target: {fileID: 6189974477040685128, guid: 5b1e9ea1ec6b21649a535663d2128dc9, type: 3} propertyPath: m_LocalPosition.y value: 0 objectReference: {fileID: 0} ... # 记录删掉的Component m_RemovedComponents: [] # 引用的原始Prefab # 注意这里的fileID!100100000是HARD CODE,表示根节点,到这个Prefab里面找不到这个fileID # 为什么要用这样的HARD CODE,我猜想是,这里只想引用到这个Prefab资源本身,而不是其中的某个"对象" # 所以需要一个特殊的数字,表示"根节点"或者"这个资源本身" m_SourcePrefab: {fileID: 100100000, guid: 5b1e9ea1ec6b21649a535663d2128dc9, type: 3} --- !u!4 &1243095720052628122 Transform: m_ObjectHideFlags: 0 ... --- !u!1 &3041560777402399037 GameObject: m_ObjectHideFlags: 0 ... --- !u!4 &3705098372205570338 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8240941246667057421} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} # Transform组件上会记录Children和Father,都是引用的Unity对象{xxx},当然,记录的都是本场景内部的Unity Object m_Children: - {fileID: 1243095720052628122} m_Father: {fileID: 4252229334167445139} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} # 关于Unity对象和引用 # MeshFilter --- !u!33 &1073524136 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1073524135} # 引用了Unity内置的一个Sphere # 引用Unity Object的写法大概就是这样,{fileID: xxx, guid: xxx, type: xxx} # 这个guid有点不普通,应该是Unity内部特殊处理过的 m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} # Raw Image # 是Unity自带的组件但是没有独立的类型,可能因为不是Unity核心而是UGUI的组件吧 # 这里再次理解一下:MonoBehaviour是Script的容器而已 --- !u!114 &21352153 MonoBehaviour: m_ObjectHideFlags: 0 ... # 应该是Raw Image的脚本 m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3} ... # 引用了一张图片 # 引用外部的Unity Object(不在这个Scene内的),需要加上guid,使用guid和fileID,共同标示唯一的一个Unity Object # [存疑]type: 3表示的是什么? m_Texture: {fileID: 2800000, guid: 3b0103a6ddb0c0947afb9cb0fa3aac92, type: 3} ... # 一个物体,在场景中需要引用一个对象,然后其引用的对象,又在一个PrefabInstance内部 # 那么场景的序列化中就会出现一个这个 --- !u!1 &1208068685 stripped GameObject: m_CorrespondingSourceObject: {fileID: 2967018447064115090, guid: 97f093d15a7e37c41a2e8b5cdc60b273, type: 3} m_PrefabInstance: {fileID: 1208068684} m_PrefabAsset: {fileID: 0}
类似Json,是一种数据标记语言。主要使用缩进(只能使用空格表示缩进)组织格式
使用ISerializationCallbackReceiver
每一个GameObject/Component都会被序列化在硬盘中,比如Component的各个配置数据等。SerializedObject就是帮助我们拿到硬盘中的数据的接口。直接访问Component,是在内存中访问;访问Component的SerializedObject,相当于访问的是“数据层”。可以控制数据“从硬盘到内存 Update”,或者“从内存写入硬盘 Apply”