TSCN 文件格式
TSCN(文本场景)文件格式表示 Godot 内部的单个场景树。与二进制的 SCN 文件不同,TSCN 具有易于人类阅读、便于使用版本控制系统进行管理的优点。
ESCN(导出场景)文件格式与 TSCN 文件格式相同,但它是用于向 Godot 表明该文件是从另一个程序导出的,不应由用户在 Godot 内部进行编辑。与 SCN 和 TSCN 文件不同,导入时,ESCN 文件会被编译为存储在 .godot/imported/
文件夹中的二进制 SCN 文件。这减少了数据大小并加快了加载速度,因为与基于文本的格式相比,二进制格式的加载更快。
为了使文件更加紧凑,属性值等于默认值的属性不会储存在场景 / 资源文件中。这些属性可以手动写入,但在储存文件时会被自动丢弃。
如果想要完整的描述,对这些文件格式的解析是在 resource_format_text.cpp 的 ResourceFormatLoaderText
类中进行处理的。
备注
Godot 4 对场景和资源文件格式做出了重大变化,引入了基于字符串的 UID 来取代递增整数的 ID。
网格,骨骼和动画数据的存储方式也不同于 Godot 3。部分变化可以参考这篇文章: Animation data rework for 4.0
Godot 4.x 中保存的场景和资源在标头中包含 format=3
,Godot 3.x 则使用 format=2
。
文件结构
TSCN 文件分为五个主要部分:
文件描述符
外部资源
内部资源
节点
连接
文件描述符类似于 [gd_scene load_steps=4 format=3 uid="uid://cecaux1sm7mo0"]
,并且应该是文件中的第一条记录。load_steps
参数等于资源总量(内部和外部)加一(文件本身)。如果该文件没有资源,则 load_steps
会被省略。即便 load_steps
不正确,引擎仍将正确加载该文件,但这会影响加载进度条和其他依赖该值的代码。
uid
是一个唯一的,基于字符串的标识符,用于代表场景。引擎利用该标识符来追踪被移动的文件,即使编辑器关闭也同样如此。脚本也可以利用 uid://
路径前缀来加载基于 UID 的资源,而不必依赖于文件系统的路径。这样,即使文件在项目中被任意移动,也可以从脚本中读取该文件,而无需对脚本进行更改。Godot 不使用外部文件来追踪 ID,因此项目中不需要中心化元数据储存位置。详细信息请参考 拉取请求 。
这些部分应按顺序出现,但可能很难区分它们。它们之间的唯一区别是该部分中所有项目的标题中的第一个元素。例如,所有外部资源的标题都应以 [ext_resource ...]
开头。
TSCN 文件可能包含以分号(;
)开头的单行注释。但是,使用 Godot 编辑器保存文件时,注释将被丢弃。TSCN 文件中的空格并不重要(字符串内的除外),但保存文件时,多余的空格将被丢弃。
文件中的条目
标题看起来像 [<resource_type> key1=value1 key2=value2 key3=value3 ...]
,其中 resource_type 是以下之一:
ext_resource
sub_resource
node
connection
每个标题下面都有零个或多个 key = value
对。值可以是类似数组、变换、颜色等复杂的数据类型。例如,一个 Node3D 将如下所示:
[node name="Cube" type="Node3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 2, 3)
场景树
场景树由节点组成!每个节点的标题由其名称、父节点和(大多数情况下)一种类型组成。例如: [node name="PlayerCamera" type="Camera" parent="Player/Head"]
其他有效的关键字包括:
instance
instance_placeholder
owner
index
(设置在树中出现的顺序;如果不存在,则继承的节点将优先于普通节点)
groups
The first node in the file, which is also the scene root, must not have a
parent="Path/To/Node"
entry in its heading. All scene files should have
exactly one scene root. If it doesn't, Godot will fail to import the file.
The parent path of other nodes should be absolute, but shouldn't contain
the scene root's name. If the node is a direct child of the scene root,
the path should be "."
. Here is an example scene tree
(but without any node content):
[node name="Player" type="Node3D"] ; The scene root
[node name="Arm" type="Node3D" parent="."] ; Parented to the scene root
[node name="Hand" type="Node3D" parent="Arm"] ; Child of "Arm"
[node name="Finger" type="Node3D" parent="Arm/Hand"] ; Child of "Hand"
小技巧
为了让文件结构更易于理解,可以将任意节点或资源保存为文件,并在外部编辑器中对其进行审查。还可以在 Godot 编辑器中进行渐进式更改,同时在外部文本编辑器自动更新启动的情况下,将 .tscn
或 .tres
文件保持打开,以观察变化内容。
以下是一个示例场景,包含一个基于 RigidBody3D 且具有碰撞的球体,视觉元素(网格和光照)和附加到 RigidBody3D 上的相机:
[gd_scene load_steps=4 format=3 uid="uid://cecaux1sm7mo0"]
[sub_resource type="SphereShape3D" id="SphereShape3D_tj6p1"]
[sub_resource type="SphereMesh" id="SphereMesh_4w3ye"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_k54se"]
albedo_color = Color(1, 0.639216, 0.309804, 1)
[node name="Ball" type="RigidBody3D"]
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("SphereShape3D_tj6p1")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("SphereMesh_4w3ye")
surface_material_override/0 = SubResource("StandardMaterial3D_k54se")
[node name="OmniLight3D" type="OmniLight3D" parent="."]
light_color = Color(1, 0.698039, 0.321569, 1)
omni_range = 10.0
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.939693, 0.34202, 0, -0.34202, 0.939693, 0, 1, 3)
节点路径
A tree structure is not enough to represent the whole scene. Godot uses a
NodePath(Path/To/Node)
structure to refer to another node or attribute of
the node anywhere in the scene tree. Paths are relative to the current node,
with NodePath(".")
pointing to the current node and NodePath("")
pointing to no node at all.
For instance, MeshInstance3D uses NodePath()
to point to its skeleton.
Likewise, Animation tracks use NodePath()
to point to node properties to
animate.
NodePath 还可能用 :property_name
后缀来指向一个属性,还可能指向向量,变换和颜色类型中的一个分量。动画资源利用这一点,来指向要动画的特定属性。例如,NodePath("MeshInstance3D:scale.x")
指向 MeshInstance3D 中的 Vector3 类型的 scale
属性中的 x
分量。
举例来说,名为``mesh``的节点``MeshInstance3D``中属性``skeleton``指向其父节点``Armature01``:
[node name="mesh" type="MeshInstance3D" parent="Armature01"]
skeleton = NodePath("..")
Skeleton3D
The Skeleton3D node inherits the Node3D node, but may also have a
list of bones described in key-value pairs in the format
bones/<id>/<attribute> = value
. The bone attributes consist of:
position
:Vector3rotation
:Quaternionscale
:Vector3
这些属性都是可选的。例如,骨骼可能只定义了 position
或 rotation
,而不定义其他属性。
具有两个骨骼的骨架节点的示例:
[node name="Skeleton3D" type="Skeleton3D" parent="PlayerModel/Robot_Skeleton" index="0"]
bones/1/position = Vector3(0.114471, 2.19771, -0.197845)
bones/1/rotation = Quaternion(0.191422, -0.0471201, -0.00831942, 0.980341)
bones/2/position = Vector3(-2.59096e-05, 0.236002, 0.000347473)
bones/2/rotation = Quaternion(-0.0580488, 0.0310587, -0.0085914, 0.997794)
bones/2/scale = Vector3(0.9276, 0.9276, 0.9276)
BoneAttachment3D
BoneAttachment3D 节点是一个中间节点,用于描述在 Skeleton 节点中以单根骨骼为父节点的某些节点。BoneAttachment 具有 bone_name = "骨骼名称"
属性,以及匹配骨骼索引的属性。
以 Skeleton 中的骨骼为父级的 Marker3D 节点示例:
[node name="GunBone" type="BoneAttachment3D" parent="PlayerModel/Robot_Skeleton/Skeleton3D" index="5"]
transform = Transform3D(0.333531, 0.128981, -0.933896, 0.567174, 0.763886, 0.308015, 0.753209, -0.632331, 0.181604, -0.323915, 1.07098, 0.0497144)
bone_name = "hand.R"
bone_idx = 55
[node name="ShootFrom" type="Marker3D" parent="PlayerModel/Robot_Skeleton/Skeleton3D/GunBone"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0)
AnimationPlayer
The AnimationPlayer node works with one or more animation libraries stored in AnimationLibrary resources. An animation library is a collection of individual Animation resources, whose structure is documented here.
This split between animations themselves and animation libraries was done in Godot 4, so that animations can be imported separately from 3D meshes, which is a common workflow in 3D animation software. See the original pull request for details.
If the library name is empty, then it acts acts the unique source of animations
for this AnimationPlayer. This allows using <animation_name>
directly to
play animations from script. If you name the library, then you must play it as
<library_name>/<animation_name>
. This ensures backwards compatibility and
keeps the existing workflow if you don't want to use multiple animation
libraries.
资源
资源是组成各个节点的元件。举例来说,MeshInstance节点中会有附带的ArrayMesh资源。该ArrayMesh资源可以在TSCN档的内部或外部。
对资源的引用由资源标题中基于字符串的唯一 ID 处理。这与每个外部资源也具有的 uid
属性不同(但子资源没有)。
External resources and internal resources are referred to with
ExtResource("id")
and SubResource("id")
, respectively. Because there
have different methods to refer to internal and external resources, you can have
the same ID for both an internal and external resource.
For example, to refer to the resource
[ext_resource type="Material" uid="uid://c4cp0al3ljsjv" path="res://material.tres" id="1_7bt6s"]
,
you would use ExtResource("1_7bt6s")
.
外部资源
External resources are links to resources not contained within the TSCN file itself. An external resource consists of a path, a type, a UID (used to map its filesystem location to a unique identifier) and an ID (used to refer to the resource in the scene file).
Godot总是生成相对于资源目录的绝对路径, 因此以 res://
为前缀, 但是相对于TSCN文件位置的路径也有效.
一些示例外部资源是:
[ext_resource type="Texture2D" uid="uid://ccbm14ebjmpy1" path="res://gradient.tres" id="2_eorut"]
[ext_resource type="Material" uid="uid://c4cp0al3ljsjv" path="material.tres" id="1_7bt6s"]
Like TSCN files, a TRES file may contain single-line comments starting with a
semicolon (;
). However, comments will be discarded when saving the resource
using the Godot editor.
Whitespace within a TRES file is not significant (except within strings), but
extraneous whitespace will be discarded when saving the file.
内部资源
TSCN文件可以包含网格, 材质和其他数据. 这些包含在文件的 内部资源 部分中. 内部资源的标题与外部资源的标题相似, 不同之处在于它没有路径. 内部资源在每个标题下还具有 键=值
对. 例如, 胶囊碰撞形状如下所示:
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_fdxgg"]
radius = 1.0
height = 3.0
一些内部资源包含到其他内部资源的链接(例如具有材质的网格). 在这种情况下, 引用的资源必须在对其的引用 之前 出现. 这意味着顺序在文件的内部资源部分中很重要.
ArrayMesh
ArrayMesh 由包含在 _surfaces
数组中的多个表面组成(注意开头的下划线)。每个表面的数据都储存在具有以下键的字典中:
aabb
: The computed axis-aligned bounding box for visibility.attribute_data
:顶点属性数据,例如法线、切线、顶点颜色、UV1、UV2和自定义顶点数据。bone_aabbs
:每个骨骼的轴对齐边界框以提高可见性。format
:表面的缓冲区格式。index_count
:表面中索引的数量。这必须与“index_data”的大小相符。index_data
: The index data, which determines which vertices fromvertex_data
are drawn.lods
:细节变化的级别,储存为数组。每个LOD等级代表数组中的两个值。第一个值是LOD等级最适合的屏幕空间百分比(边缘长度);第二个值是应为给定LOD等级绘制的索引列表。material
:绘制表面时所使用的材质。name
:表面的名称。这可以在脚本中使用,并从3D DCC汇入。primitive
: The surface's primitive type, matching theMesh.PrimitiveType
Godot enum.0
= points,1
= lines,2
= line strip,3
= triangles (most common),4
= triangle strip.skin_data
:骨骼重量数据。vertex_count
:表面中的顶点数。这必须与“vertex_data”的大小相符。vertex_data
:顶点位置数据。
Here's an example of an ArrayMesh saved to its own .tres
file. Some fields were shortened with ...
for brevity:
[gd_resource type="ArrayMesh" load_steps=2 format=3 uid="uid://dww8o7hsqrhx5"]
[ext_resource type="Material" path="res://player/model/playerobot.tres" id="1_r3bjq"]
[resource]
resource_name = "player_Sphere_016"
_surfaces = [{
"aabb": AABB(-0.207928, 1.21409, -0.14545, 0.415856, 0.226569, 0.223374),
"attribute_data": PackedByteArray(63, 121, ..., 117, 63),
"bone_aabbs": [AABB(0, 0, 0, -1, -1, -1), ..., AABB(-0.207928, 1.21409, -0.14545, 0.134291, 0.226569, 0.223374)],
"format": 7191,
"index_count": 1224,
"index_data": PackedByteArray(30, 0, ..., 150, 4),
"lods": [0.0382013, PackedByteArray(33, 1, ..., 150, 4)],
"material": ExtResource("1_r3bjq"),
"name": "playerobot",
"primitive": 3,
"skin_data": PackedByteArray(15, 0, ..., 0, 0),
"vertex_count": 1250,
"vertex_data": PackedByteArray(196, 169, ..., 11, 38)
}]
blend_shape_mode = 0
动画
Each animation has the following properties:
length
:动画的长度(以秒为单位)。请注意,关键帧可以放置在[0; length]
的区间之外,但根据所选的插值模式这样可能没有效果。loop_mode
:0
= no looping,1
= wrap-around looping,2
= clamped looping.step
:在编辑器中编辑此动画时所使用的步长。该属性只在编辑器中可用;它不会以任何方式影响动画播放。
每个轨道均由格式为 tracks/<id>/<attribute>
的键值对列表描述。每个轨道包括:
type
:轨道的类型。这个属性定义了该轨道可以动画化哪种属性,以及如何在编辑器中向用户公开它。有效类型为value
(通用属性轨道)、position_3d
、rotation_3d
、scale_3d
、blend_shape
(优化的 3D 动画轨道)、method
(方法调用轨道)、bezier
(贝塞尔曲线轨道)、audio
(音频播放轨道)、animation
(播放其他动画的轨道)。imported
:如果轨道是从导入的 3D 场景创建的,则为true
;如果轨道是由用户在 Godot 编辑器中或使用脚本手动创建的,则为false
。enabled
:如果轨道有效的(即被启用),则为true
;如果轨道在编辑器中被禁用,则为false
。path
:将受轨道影响的节点属性的路径。该属性写在节点路径后面,并使用:
分隔符。interp
:要使用的插值模式。0
= 最近(nearest),1
= 线性(linear),2
= 立方(cubic),3
= 线性角度(linear angle),4
= 立方角度(cubic angle)。loop_wrap
:true
if the track is designed to wrap around when the animation is looping,false
if the track clamps to the first/last keyframes.keys
:动画轨道的值。该属性的结构取决于type
。
这是一个包含 AnimationPlayer 的场景,它使用通用属性轨道,随时间推进缩小立方体。由于未使用 AnimationLibrary 工作流程,此动画库的名称为空(但动画仍指定为 scale_down
名称)。请注意,为了简洁起见,没有在此 AnimationPlayer 中创建 RESET
轨道:
[gd_scene load_steps=4 format=3 uid="uid://cdyt3nktp6y6"]
[sub_resource type="Animation" id="Animation_r2qdp"]
resource_name = "scale_down"
length = 1.5
loop_mode = 2
step = 0.05
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Box:scale")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Vector3(1, 1, 1), Vector3(0, 0, 0)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_4qx36"]
_data = {
"scale_down": SubResource("Animation_r2qdp")
}
[sub_resource type="BoxMesh" id="BoxMesh_u688r"]
[node name="Node3D" type="Node3D"]
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
autoplay = "scale_down"
libraries = {
"": SubResource("AnimationLibrary_4qx36")
}
[node name="Box" type="MeshInstance3D" parent="."]
mesh = SubResource("BoxMesh_u688r")
对于通用属性 value
轨道,keys
是一个字典,其中包含 3 个数组。三个数组分别是:位置(position)位于 times
(PackedFloat32Array)中;缓动值(easing value)位于 transitions
(PackedFloat32Array)中;值(value)位于 values
(Array)中。还有一个附加的 update
属性,它是一个整数,其值代表的含义分别是: 0
= 连续(continuous),1
= 离散(discrete),2
= 捕获(capture)。
这是第二个动画资源,它利用了 3D 位置和 3D 旋转轨道。这些轨道(除了 3D 缩放轨道)取代了 Godot 3 中的 Transform 轨道。它们经过优化,可以快速播放,并且可以选择进行压缩。
这些优化轨道类型的缺点是,它们无法使用自定义缓动值。相反地,所有关键帧都使用线性插值。也就是说,你仍然可以通过更改轨道的插值模式,来选择对给定轨道中的所有关键帧使用最近插值或三次插值。
[sub_resource type="Animation" id="Animation_r2qdp"]
resource_name = "move_and_rotate"
length = 1.5
loop_mode = 2
step = 0.05
tracks/0/type = "position_3d"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Box")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = PackedFloat32Array(0, 1, 0, 0, 0, 1.5, 1, 1.5, 1, 0)
tracks/1/type = "rotation_3d"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Box")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = PackedFloat32Array(0, 1, 0.211, -0.047, 0.211, 0.953, 1.5, 1, 0.005, 0.976, -0.216, 0.022)
对于 3D 位置、旋转和缩放轨道,key
是一个将所有值都存储在序列中的 PackedFloat32Array。
在下面的视觉指南中,T
是自动画开始以来关键帧的时间(以秒为单位),E
是关键帧的过渡(当前始终为 1
)。对于 3D 位置和比例轨道,X
、 Y
、 Z
是 Vector3 的坐标。对于 3D 旋转轨道,X
、 Y
、 Z
和 W
是四元数的坐标。
# For 3D position and scale, which use Vector3:
tracks/<id>/keys = PackedFloat32Array(T, E, X, Y, Z, T, E, X, Y, Z, ...)
# For 3D rotation, which use Quaternion:
tracks/<id>/keys = PackedFloat32Array(T, E, X, Y, Z, W, T, E, X, Y, Z, W, ...)