TSCN 文件格式
TSCN(文本场景)文件格式表示 Godot 内部的单个场景树。与二进制的 SCN 文件不同,TSCN 具有易于人类阅读、便于使用版本控制系统进行管理的优点。
The ESCN (exported scene) file format is identical to the TSCN file format, but is used to indicate to Godot that the file has been exported from another program and should not be edited by the user from within Godot. Unlike SCN and TSCN files, during import, ESCN files are compiled to binary SCN files stored inside the .godot/imported/
folder. This reduces the data size and speeds up loading, as binary formats are faster to load compared to text-based formats.
To make files more compact, properties equal to the default value are not stored in scene/resource files. It is possible to write them manually, but they will be discarded when saving the file.
如果想要完整的描述,对这些文件格式的解析是在 resource_format_text.cpp 的 ResourceFormatLoaderText
类中进行处理的。
备注
The scene and resource file formats have changed significantly in Godot 4, with the introduction of string-based UIDs to replace incremental integer IDs.
Mesh, skeleton and animation data is also stored differently compared to Godot 3. You can read about some of the changes in this article: Animation data rework for 4.0
Scenes and resources saved with Godot 4.x contain format=3
in their header, whereas Godot 3.x uses format=2
instead.
文件结构
TSCN 文件分为五个主要部分:
文件描述符
外部资源
内部资源
节点
连接
The file descriptor looks like [gd_scene load_steps=4 format=3 uid="uid://cecaux1sm7mo0"]
and should be the first entry in the file. The load_steps
parameter is equal to the total amount of resources (internal and external) plus one (for the file itself). If the file has no resources, load_steps
is omitted. The engine will still load the file correctly if load_steps
is incorrect, but this will affect loading bars and any other piece of code relying on that value.
uid
is a unique string-based identifier representing the scene. This is used by the engine to track files that are moved around, even while the editor is closed. Scripts can also load UID-based resources using the uid://
path prefix to avoid relying on filesystem paths. This makes it possible to move around a file in the project, but still be able to load it in scripts without having to modify the script. Godot does not use external files to keep track of IDs, which means no central metadata storage location is required within the project. See this pull request for detailed information.
These sections should appear in order, but it can be hard to distinguish them. The only difference between them is the first element in the heading for all of the items in the section. For example, the heading of all external resources should start with [ext_resource ...]
.
A TSCN file may contain single-line comments starting with a semicolon (;
). However, comments will be discarded when saving the file using the Godot editor. Whitespace within a TSCN file is not significant (except within strings), but extraneous whitespace will be discarded when saving the file.
文件中的条目
A heading looks like [<resource_type> key1=value1 key2=value2 key3=value3 ...]
where resource_type is one of:
ext_resource
sub_resource
node
connection
Below every heading comes zero or more key = value
pairs. The values can be complex datatypes such as Arrays, Transforms, Colors, and so on. For example, a Node3D looks like:
[node name="Cube" type="Node3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 2, 3)
场景树
The scene tree is made up of… nodes! The heading of each node consists of its name, parent and (most of the time) a type. For example: [node name="PlayerCamera" type="Camera" 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
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"
小技巧
To make the file structure easier to grasp, you can save a file with any given node or resource and then inspect it yourself in an external editor. You can also make incremental changes in the Godot editor, and keep an external text editor open on the .tscn
or .tres
file with auto-reload enabled to see what changes.
Here is an example of a scene containing a RigidBody3D-based ball with collision, visuals (mesh + light) and a camera parented to the 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 can also point to a property using a :property_name
suffix, and even point to a specific component for vector, transform and color types. This is used by Animation resources to point to specific properties to animate. For example, NodePath("MeshInstance3D:scale.x")
points to the x
component of the scale
Vector3 property in MeshInstance3D.
举例来说,名为``mesh``的节点``MeshInstance3D``中属性``skeleton``指向其父节点``Armature01``:
[node name="mesh" type="MeshInstance3D" parent="Armature01"]
skeleton = NodePath("..")
Skeleton3D
Skeleton3D 节点继承 Node3D 节点,但也可能具有以 bones/<id>/<attribute> = value
格式的键值来描述的骨骼列表。骨骼属性包括:
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
class_AnimationPlayer节点与储存在class_AnimationLibrary资源中的一个或多个动画库搭配使用。动画库是各个:ref:class_Animation`资源的集合,其结构记录在:ref:`此处<doc_tscn_animation>。
动画本身和动画库之间的这种分离是在Godot 4中完成的,以便动画可以与3D网格分开汇入,这是3D动画软件中的常见工作流程。有关详细信息,请参阅“原始拉取请求<https://github.com/godotengine/godot/pull/59980>”。
如果库名称为空,则它充当此AnimationPlayer的唯一动画来源。这允许直接使用``<animation_name>``从脚本播放动画。如果你命名库,则必须将其播放为``<library_name>/<animation_name>``。如果你不想使用多个动画库,这可以确保向后兼容性并保留现有的工作流程。
资源
资源是组成各个节点的元件。举例来说,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”:索参数据,它决定从“vertex_data”中绘制哪些顶点。
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
:顶点位置数据。
这是保存到自己的.tres档案中的ArrayMesh的示例。为了简洁起见,一些字段被缩短为“…”:
[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, ...)