TSCN 文件格式

TSCN(文本场景)文件格式表示 Godot 内部的单个场景树。与二进制的 SCN 文件不同,TSCN 具有易于人类阅读、便于使用版本控制系统进行管理的优点。

ESCN(导出场景)文件格式与 TSCN 文件格式相同,但是用于向 Godot 表明该文件是从另一个程序导出的,不应由用户在 Godot 内部进行编辑。与 SCN 和 TSCN 文件不同,导入时,ESCN 文件会被编译为二进制的 SCN 文件存储到 .import/ 文件夹中。这样可以减小数据体积、提高加载速度,因为相较于基于文本的格式,二进制格式的加载更快。

如果想要完整的描述,对这些文件格式的解析是在 resource_format_text.cppResourceFormatLoaderText 类中进行处理的。

文件结构

TSCN 文件分为五个主要部分:

  1. 文件描述符

  2. 外部资源

  3. 内部资源

  4. 节点

  5. 连接

类似 [gd_scene load_steps=3 format=2] 的就是文件描述符,应该是文件的第一条记录。这里的 load_steps 参数等于资源总数(包括内部和外部)加一(文件本身)。文件中不包含资源时 load_steps 会被省略。即便 load_steps 不正确,引擎仍然能够正确加载文件,但是加载进度条和其它一些使用该值的代码会受影响。

这些部分应按顺序显示, 但是可能很难区分它们. 它们之间的唯一区别是该部分中所有项目的标题中的第一个元素. 例如, 所有外部资源的标题都应以 [ext_resource .....] 开头.

TSCN文件可能包含以分号(;)开头的单行注释. 但是, 使用Godot编辑器保存文件时, 注释将被丢弃.

文件中的条目

标题看起来像 [<resource_type> key=value key=value key=value ...] , 其中 resource_type 是以下之一:

  • ext_resource

  • sub_resource

  • node

  • connection

在每个标题下方都有零个或多个 key = value 对. 值可以是复杂的数据类型, 例如Arrays, Transforms, Colors等. 例如, 一个Spatial节点如下所示:

  1. [node name="Cube" type="Spatial" parent="."]
  2. transform=Transform( 1.0, 0.0, 0.0 ,0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 )

场景树

场景树由……节点组成!每个节点的标题由其名称, 父级和(大多数情况下)一种类型组成. 例如 [node type="Camera" name="PlayerCamera" parent="Player/Head"]

其他有效的关键字包括:

  • instance

  • instance_placeholder

  • owner

  • index (sets the order of appearance in the tree. If absent, inherited nodes will take precedence over plain ones)

  • groups

文件中的第一个节点(也是场景根)的标题中不得包含 parent=Path/To/Node 条目. 所有场景文件应完全具有 一个 场景根. 否则,Godot将无法导入文件. 其他节点的父路径应为绝对路径, 但不应包含场景根名称. 如果节点是场景根的直接子代, 则路径应为 ".". 这是一个示例场景树(但没有任何节点内容):

  1. [node name="Player" type="Spatial"] ; The scene root
  2. [node name="Arm" parent="." type="Spatial"] ; Parented to the scene root
  3. [node name="Hand" parent="Arm" type="Spatial"]
  4. [node name="Finger" parent="Arm/Hand" type="Spatial"]

与内部资源类似, 每个节点的文档当前不完整. 幸运的是, 可以很容易地找到, 因为您可以简单地保存文件并将该节点包含其中. 一些示例节点是:

  1. [node type="CollisionShape" name="SphereCollision" parent="SpherePhysics"]
  2. shape = SubResource(8)
  3. transform = Transform( 1.0 , 0.0 , -0.0 , 0.0 , -4.371138828673793e-08 , 1.0 , -0.0 , -1.0 , -4.371138828673793e-08 ,0.0 ,0.0 ,-0.0 )
  4. [node type="MeshInstance" name="Sphere" parent="SpherePhysics"]
  5. mesh = SubResource(9)
  6. transform = Transform( 1.0 , 0.0 , -0.0 , 0.0 , 1.0 , -0.0 , -0.0 , -0.0 , 1.0 ,0.0 ,0.0 ,-0.0 )
  7. [node type="OmniLight" name="Lamp" parent="."]
  8. light_energy = 1.0
  9. light_specular = 1.0
  10. transform = Transform( -0.29086464643478394 , -0.7711008191108704 , 0.5663931369781494 , -0.05518905818462372 , 0.6045246720314026 , 0.7946722507476807 , -0.9551711678504944 , 0.199883371591568 , -0.21839118003845215 ,4.076245307922363 ,7.3235554695129395 ,-1.0054539442062378 )
  11. omni_range = 30
  12. shadow_enabled = true
  13. light_negative = false
  14. light_color = Color( 1.0, 1.0, 1.0, 1.0 )
  15. [node type="Camera" name="Camera" parent="."]
  16. projection = 0
  17. near = 0.10000000149011612
  18. fov = 50
  19. transform = Transform( 0.6859206557273865 , -0.32401350140571594 , 0.6515582203865051 , 0.0 , 0.8953956365585327 , 0.44527143239974976 , -0.7276763319969177 , -0.3054208755493164 , 0.6141703724861145 ,14.430776596069336 ,10.093015670776367 ,13.058500289916992 )
  20. far = 100.0

节点路径

树形结构不足以代表整个场景。Godot 使用 NodePath(Path/To/Node) 结构来引用该节点的属性或场景树中任何位置的另一个节点。例如,MeshInstance 使用 NodePath() 指向其骨架。同样,动画轨迹使用 NodePath() 指向要动画的节点属性。

  1. [node name="mesh" type="MeshInstance" parent="Armature001"]
  2. mesh = SubResource(1)
  3. skeleton = NodePath("..:")
  1. [sub_resource id=3 type="Animation"]
  2. ...
  3. tracks/0/type = "transform
  4. tracks/0/path = NodePath("Cube:")
  5. ...

骨架

Skeleton 节点继承了 Spatial 节点,但也可能具有以 bones/Id/Attribute=Value 格式在键值对中描述的骨骼列表。骨骼的属性有:

  • name

  • parent

  • rest

  • pose

  • enabled

  • bound_children

  1. name 必须是每个骨骼的第一个属性.

  2. parent 是骨骼列表中父骨骼的索引, 使用父索引, 骨骼列表将构建到骨骼树中.

  3. rest 是处于其“放松”位置的骨骼的变换矩阵。

  4. pose 是姿势矩阵;以 rest 为基础。

  5. bound_childrenNodePath() 的列表,该列表指向该骨骼的 BoneAttachment。

具有两个骨骼的骨架节点的示例:

  1. [node name="Skeleton" type="Skeleton" parent="Armature001" index="0"]
  2. bones/0/name = "Bone.001"
  3. bones/0/parent = -1
  4. bones/0/rest = Transform( 1, 0, 0, 0, 0, -1, 0, 1, 0, 0.038694, 0.252999, 0.0877164 )
  5. bones/0/pose = Transform( 1.0, 0.0, -0.0, 0.0, 1.0, -0.0, -0.0, -0.0, 1.0, 0.0, 0.0, -0.0 )
  6. bones/0/enabled = true
  7. bones/0/bound_children = [ ]
  8. bones/1/name = "Bone.002"
  9. bones/1/parent = 0
  10. bones/1/rest = Transform( 0.0349042, 0.99939, 0.000512929, -0.721447, 0.0248417, 0.692024, 0.691589, -0.0245245, 0.721874, 0, 5.96046e-08, -1.22688 )
  11. bones/1/pose = Transform( 1.0, 0.0, -0.0, 0.0, 1.0, -0.0, -0.0, -0.0, 1.0, 0.0, 0.0, -0.0 )
  12. bones/1/enabled = true
  13. bones/1/bound_children = [ ]

BoneAttachment

BoneAttachment 节点是一个中间节点,用于描述在 Skeleton 节点中以单根骨骼为父节点的某些节点。BoneAttachment 具有 bone_name=NameOfBone 属性,并且作为父级的相应骨骼在其 bound_children 列表中具有 BoneAttachment 节点。

在以 Skeleton 中的某根骨骼为父级的 MeshInstance 的示例:

  1. [node name="Armature" type="Skeleton" parent="."]
  2. transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -0.0219986, 0.0125825, 0.0343127)
  3. bones/0/name = "Bone"
  4. bones/0/parent = -1
  5. bones/0/rest = Transform(1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0)
  6. bones/0/pose = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0)
  7. bones/0/enabled = true
  8. bones/0/bound_children = [NodePath("BoneAttachment:")]
  9. [node name="BoneAttachment" type="BoneAttachment" parent="Armature"]
  10. bone_name = "Bone"
  11. [node name="Cylinder" type="MeshInstance" parent="Armature/BoneAttachment"]
  12. mesh = SubResource(1)
  13. transform = Transform(1.0, 0.0, 0.0, 0.0, 1.86265e-09, 1.0, 0.0, -1.0, 0.0, 0.0219986, -0.0343127, 2.25595)

AnimationPlayer

AnimationPlayer用作动画库. 它以 anim/Name=SubResource(ResourceId) 格式存储动画;每行都引用一个Animation资源. 所有动画资源都使用AnimationPlayer的根节点. 根节点存储为 root_node=NodePath(Path/To/Node).

  1. [node name="AnimationPlayer" type="AnimationPlayer" parent="." index="1"]
  2. root_node = NodePath("..")
  3. autoplay = ""
  4. playback_process_mode = 1
  5. playback_default_blend_time = 0.0
  6. playback_speed = 1.0
  7. anims/default = SubResource( 2 )
  8. blend_times = [ ]

资源

资源是组成节点的组件. 例如,MeshInstance节点将具有附带的ArrayMesh资源. ArrayMesh资源可以是TSCN文件的内部或外部资源.

对资源的引用由资源标题中的 id 数字处理. 外部资源和内部资源分别以 ExtResource(id)SubResource(id) 引用. 因为引用内部和外部资源的方法不同, 所以内部和外部资源的ID可以相同.

例如, 要引用资源 [ext_resource id=3 type="PackedScene" path=....], 则应使用 ExtResource(3).

外部资源

外部资源是指向TSCN文件本身未包含的资源的链接. 外部资源由路径, 类型和ID组成.

Godot总是生成相对于资源目录的绝对路径, 因此以 res:// 为前缀, 但是相对于TSCN文件位置的路径也有效.

一些示例外部资源是:

  1. [ext_resource path="res://characters/player.dae" type="PackedScene" id=1]
  2. [ext_resource path="metal.tres" type="Material" id=2]

像TSCN文件一样,TRES文件可能包含以分号(;)开头的单行注释. 但是, 使用Godot编辑器保存资源时, 注释将被丢弃.

内部资源

TSCN文件可以包含网格, 材质和其他数据. 这些包含在文件的 内部资源 部分中. 内部资源的标题与外部资源的标题相似, 不同之处在于它没有路径. 内部资源在每个标题下还具有 键=值 对. 例如, 胶囊碰撞形状如下所示:

  1. [sub_resource type="CapsuleShape" id=2]
  2. radius = 0.5
  3. height = 3.0

一些内部资源包含到其他内部资源的链接(例如具有材质的网格). 在这种情况下, 引用的资源必须在对其的引用 之前 出现. 这意味着顺序在文件的内部资源部分中很重要.

遗憾的是, 关于这些子资源的格式的文档并不完整, 通过检查已保存的资源文件可以找到一些示例, 但是只有通过查看Godot的源码才能找到其他示例.

ArrayMesh

ArrayMesh由多个表面组成, 每个表面的格式为 surface\Index={}. 每个表面都是一组顶点和一种材质.

TSCN文件支持两种表面格式:

  1. 对于旧格式, 每个表面都有三个基本键:
  • primitive

  • arrays

  • morph_arrays

    1. primitive 是一个枚举变量, primitive=4 是被频繁使用的 PRIMITIVE_TRIANGLES.

    2. arrays 是一个二维数组,它包含:

      1. 顶点位置数组

      2. Normals array

      3. 切线数组

      4. 顶点颜色数组

      5. UV数组1

      6. UV数组2

      7. 骨骼索引数组

      8. 骨骼权重数组

      9. 顶点索引数组

    3. morph_arrays 是一个morph数组. 每个morph恰好一个没有顶点索引数组的 arrays.

ArrayMesh的一个示例:

  1. [sub_resource id=1 type="ArrayMesh"]
  2. surfaces/0 = {
  3. "primitive":4,
  4. "arrays":[
  5. Vector3Array(0.0, 1.0, -1.0, 0.866025, -1.0, -0.5, 0.0, -1.0, -1.0, 0.866025, 1.0, -0.5, 0.866025, -1.0, 0.5, 0.866025, 1.0, 0.5, -8.74228e-08, -1.0, 1.0, -8.74228e-08, 1.0, 1.0, -0.866025, -1.0, 0.5, -0.866025, 1.0, 0.5, -0.866025, -1.0, -0.5, -0.866025, 1.0, -0.5),
  6. Vector3Array(0.0, 0.609973, -0.792383, 0.686239, -0.609973, -0.396191, 0.0, -0.609973, -0.792383, 0.686239, 0.609973, -0.396191, 0.686239, -0.609973, 0.396191, 0.686239, 0.609973, 0.396191, 0.0, -0.609973, 0.792383, 0.0, 0.609973, 0.792383, -0.686239, -0.609973, 0.396191, -0.686239, 0.609973, 0.396191, -0.686239, -0.609973, -0.396191, -0.686239, 0.609973, -0.396191),
  7. null, ; No Tangents,
  8. null, ; no Vertex Colors,
  9. null, ; No UV1,
  10. null, ; No UV2,
  11. null, ; No Bones,
  12. null, ; No Weights,
  13. IntArray(0, 2, 1, 3, 1, 4, 5, 4, 6, 7, 6, 8, 0, 5, 9, 9, 8, 10, 11, 10, 2, 1, 10, 8, 0, 1, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 5, 0, 3, 0, 9, 11, 9, 5, 7, 9, 10, 11, 11, 2, 0, 10, 1, 2, 1, 6, 4, 6, 1, 8)
  14. ],
  15. "morph_arrays":[]
  16. }

动画

动画资源由轨道组成. 此外, 它还有 length, loopstep 应用于所有轨道.

  1. lengthstep 都是以秒为单位的持续时间.

每个轨道由格式为 tracks/Id/Attribute 的键值对列表描述. 每个轨道包括:

  • type

  • path

  • interp

  • keys

  • loop_wrap

  • imported

  • enabled

  1. type 必须是每个轨道的第一个属性. type 的值可以是:

    • transform

    • value

    • method

  2. path 的格式为 NodePath(Path/To/Node:attribute). 它是动画化节点或属性的路径, 相对于AnimationPlayer中定义的根节点.

  3. interp 是对关键帧进行插值的方法. 它是一个枚举变量, 有以下值之一:

    • 0 (常数)

    • 1 (线性)

    • 2 (立方)

  4. keys 对应于关键帧. 它显示为 PoolRealArray(), 但对于具有不同类型的轨道可能具有不同的结构.

    • 变换轨道使用 keys 中的每 12 个实数来描述关键帧。第一个数字是时间戳,第二个数字是转换,后跟的三个数字是平移向量,再跟的四数字是旋转四元数(X、Y、Z、W),最后是三数字缩放向量。变换轨道中的默认转换为 1.0。
  1. [sub_resource type="Animation" id=2]
  2. length = 4.95833
  3. loop = false
  4. step = 0.1
  5. tracks/0/type = "transform"
  6. tracks/0/path = NodePath("Armature001")
  7. tracks/0/interp = 1
  8. tracks/0/loop_wrap = true
  9. tracks/0/imported = true
  10. tracks/0/enabled = true
  11. tracks/0/keys = PoolRealArray( 0, 1, -0.0358698, -0.829927, 0.444204, 0, 0, 0, 1, 0.815074, 0.815074, 0.815074, 4.95833, 1, -0.0358698, -0.829927, 0.444204, 0, 0, 0, 1, 0.815074, 0.815074, 0.815074 )
  12. tracks/1/type = "transform"
  13. tracks/1/path = NodePath("Armature001/Skeleton:Bone.001")
  14. tracks/1/interp = 1
  15. tracks/1/loop_wrap = true
  16. tracks/1/imported = true
  17. tracks/1/enabled = false
  18. tracks/1/keys = PoolRealArray( 0, 1, 0, 5.96046e-08, 0, 0, 0, 0, 1, 1, 1, 1, 4.95833, 1, 0, 5.96046e-08, 0, 0, 0, 0, 1, 1, 1, 1 )