Zhonghui

每个不曾起舞的日子,都是对生命的辜负

User Tools

Site Tools


软件:unity:序列化为yaml

Unity数据序列化为YAML

Unity中大部分的内置资源,都是使用YAML描述的

Unity 使用自定义的优化 YAML 库(称为 UnityYAML)。UnityYAML 库不支持完整的 YAML 规范


游戏引擎和序列化

游戏对象还没有在Runtime初始化的时候就有了数据,数据需要加载。在C++里面,类是定义,没有数据。所以游戏引擎就是要加一层数据,数据序列化/反序列化就是保存/加载的过程

外链资料:

  1. Unity学习笔记 - Assets, Objects and Serialization https://www.cnblogs.com/twjcnblog/p/5673309.html

类型枚举 / Object Type ID

关于这个东西是个啥,参考后面的序列化示例

完整的表可以参考官方的文档:
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

Meta文件

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和原来一致

Guid和fileID

  1. GUID是资源层级的,整个工程内部不会重复,GUID标示的是资源
  2. fileID是文件层级的,在一个资源文件内部,可能会有多个fileID,这些fileID是不会重复的,fileID标示的是Unity Object
  3. 引用Unity对象都是通过ID进行的,一般都需要fileID,引用外部其他文件内的对象的时候需要GUID
  4. fileID也叫LocalID

Asset和GameObject

我们在代码中写一个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}

YAML

类似Json,是一种数据标记语言。主要使用缩进(只能使用空格表示缩进)组织格式

复杂数据结构序列化

使用ISerializationCallbackReceiver

SerializedObject

每一个GameObject/Component都会被序列化在硬盘中,比如Component的各个配置数据等。SerializedObject就是帮助我们拿到硬盘中的数据的接口。直接访问Component,是在内存中访问;访问Component的SerializedObject,相当于访问的是“数据层”。可以控制数据“从硬盘到内存 Update”,或者“从内存写入硬盘 Apply”

/var/www/DokuWikiStick/dokuwiki/data/pages/软件/unity/序列化为yaml.txt · Last modified: 2023/05/16 09:47 by zh