TSCN 檔案格式

TSCN(Text SCeNe)檔案格式代表 Godot 中的一個單一場景樹。與二進位 SCN 檔不同,TSCN 檔案主要具備可供人閱讀、方便版本控制的優勢。

ESCN(Exported SCeNe)檔案格式與 TSCN 完全相容,但用於標示此檔案為從其他程式匯出,不應在 Godot 內被編輯。與 SCN/TSCN 不同,ESCN 在匯入時會被編譯為二進位 SCN 檔,儲存在 .godot/imported/ 資料夾中,這有助於減少資料大小並加速載入。

為了讓檔案更精簡,等於預設值的屬性不會儲存在場景/資源檔內。雖然可以手動寫入,但在儲存時會被自動移除。

如需完整說明,請參考 ResourceFormatLoaderText 類別中負責剖析的 resource_format_text.cpp 檔案。

備註

Godot 4 的場景與資源檔案格式有重大變動,新增以字串為基礎的 UID,以取代以往的遞增整數 ID。

與 Godot 3 相比,網格、骨架與動畫資料的儲存方式也不同。部分變更可參考:4.0 的動畫資料重構

以 Godot 4.x 儲存的場景與資源檔案標頭會包含 format=3,而 Godot 3.x 為 format=2

檔案結構

TSCN 檔案主要分為五個區段:

  1. 檔案描述

  2. 外部資源

  3. 內部資源

  4. 節點

  5. 連線

檔案描述元會像 [gd_scene load_steps=4 format=3 uid="uid://xxx"],必須放在檔案最前方。load_steps 數值為所有(內外部)資源數加一(自身),若無資源則可省略。即使 load_steps 值不正確,Godot 仍可正確載入檔案,但這會影響載入進度條與依賴該數值的程式。

uid 是用來代表場景的唯一字串識別碼。引擎會用來追蹤檔案位置變動,即使編輯器關閉也一樣。腳本可透過 uid:// 路徑前綴載入 UID 資源,無需依賴檔案系統路徑,方便專案內移動資源又不需修改腳本。Godot 不用外部檔案記錄 ID,因此不需專案內存在中央 metadata。詳情見此 pull request:<https://github.com/godotengine/godot/pull/50786>。

這些區段應依順序出現,但區分上可能有些困難。區段的差別主要在於每個專案標頭的首個元素。例如,所有外部資源的標頭皆以 [ext_resource ...] 開始。

TSCN 檔案可包含以分號(;)開頭的單行註解,但以 Godot 編輯器儲存時註解會被移除。檔案內空白字元(除字串外)也會在儲存時去除。

檔案中的專案

每個標頭格式為 [<resource_type> key1=value1 key2=value2 ...],其中 resource_type 可為:

  • ext_resource

  • sub_resource

  • node

  • connection

每個標頭下方可有 0 個或多個 key = value 索引鍵/值組。值可為如 Array、Transform、Color 等複雜型別。例如,一個 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

檔案的第一個節點即為場景根節點,標頭**不得**包含 parent="Path/To/Node"。所有場景檔應恰有*一個*根節點,否則 Godot 會匯入失敗。其他節點的父路徑必須為絕對路徑,但不可包含根節點名稱。若為根節點的直接子節點,路徑應為 "."。以下為範例樹狀結構(無內容):

[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)

NodePath

單純的樹狀結構不足以表現整個場景。Godot 以 NodePath(Path/To/Node) 結構,在場景樹中參照其他節點或其屬性。路徑為相對於當前節點,NodePath(".") 表示自己,NodePath("") 則表示無對應節點。

例如,MeshInstance3D 用 NodePath() 指向其骨架節點;動畫軌道同樣以 NodePath() 指向要動畫化的節點屬性。

NodePath 也能用 :property_name 後綴指向屬性,甚至細到向量、變換、顏色類型的特定分量。動畫資源會利用這點,指向要動畫化的特定屬性。例如: NodePath("MeshInstance3D:scale.x") 會指向 MeshInstance3D 節點中 scale (Vector3 屬性)的 x 分量。

舉例來說,名為 mesh 的 MeshInstance3D 節點的 skeleton 屬性會指向其父節點 Armature01

[node name="mesh" type="MeshInstance3D" parent="Armature01"]
skeleton = NodePath("..")

Skeleton3D

Skeleton3D 節點繼承自 Node3D,同時也會以 bones/<id>/<attribute> = value 的格式記錄骨骼清單。骨骼屬性有:

  • position:Vector3

  • rotation:Quaternion

  • scale:Vector3

這些屬性皆為選填。例如骨骼可以只定義 positionrotation,不必全部設值。

以下為含兩個骨頭的骨架節點範例:

[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 = "骨骼名稱" 屬性,以及對應骨骼索引的屬性。

以下為 Marker3D 附掛於 Skeleton 骨骼下的範例:

[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

AnimationPlayer 節點會搭配一個或多個儲存在 AnimationLibrary 資源中的動畫庫使用。動畫庫即為多個 Animation 資源的集合,其結構可參考 此處

Godot 4 將動畫與動畫庫分離,讓動畫能獨立於 3D 網格匯入,這也是 3D 動畫軟體常見的流程。詳見 原始 pull request

若動畫庫名稱為空,則該庫就是此 AnimationPlayer 的唯一動畫來源,可直接用 <animation_name> 於腳本播放動畫。若有命名,則必須用 <library_name>/<animation_name> 播放。這設計可確保向下相容,維持單一動畫庫的舊有工作流程。

資源

資源是組成節點的各種元件。例如 MeshInstance3D 節點會有對應的 ArrayMesh 資源。這些 ArrayMesh 資源可存在於 TSCN 檔內或外部。

參照資源時會以資源標頭內唯一字串 ID 指定。這與外部資源的 uid 屬性不同(子資源則沒有 uid)。

外部資源和內部資源分別用 ExtResource("id")SubResource("id") 指向。內外部資源參照方式不同,因此兩者可有相同的 ID。

舉例,若有 [ext_resource type="Material" uid="..." path="res://material.tres" id="1_7bt6s"],即可用 ExtResource("1_7bt6s") 參照該資源。

外部資源

外部資源是指連結到 TSCN 檔案外部的資源。包含路徑、型別、UID(用於唯一識別)及 ID(於場景檔內參照用)。

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"]

與 TSCN 相同,TRES 檔可用分號(;)作單行註解,但以 Godot 編輯器儲存時註解會被清除。檔內空白(字串除外)也會被移除。

內部資源

TSCN 檔案可包含網格、材質等資料,這些都放在*內部資源*區段。內部資源標頭與外部資源類似,但不含路徑。每個標頭下也有 key=value 組。例如,一個膠囊碰撞形狀可能如下:

[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_fdxgg"]
radius = 1.0
height = 3.0

部分內部資源會連結至其他內部資源(如網格需指定材質)。這時被參照的資源必須出現在參照者*之前*,因此內部資源順序很重要。

ArrayMesh

ArrayMesh 由 _surfaces 陣列(底線開頭)中的多個表面組成。每個表面資料都以字典儲存,下列為主要鍵值:

  • aabb:已計算的軸對齊包圍盒,用於可見性判斷。

  • attribute_data:頂點屬性資料,例如法線、切線、頂點顏色、UV1、UV2 與自訂頂點資料。

  • bone_aabbs:每個骨骼的軸對齊包圍盒,用於判斷可見性。

  • format:表面的緩衝區格式。

  • index_count:表面索引數量。必須與 index_data 長度一致。

  • index_data:索引資料,決定從 vertex_data 中繪製哪些頂點。

  • lods:細節層級(Level of Detail, LOD)變化,儲存為陣列。每層包含兩個值:第一個是適用的螢幕空間百分比(邊長),第二個是該 LOD 所需繪製的索引清單。

  • material:繪製表面所用的材質。

  • name:表面名稱。可於腳本中使用,也可由 3D DCC 匯入。

  • primitive:表面原始類型,對應 Godot 的 Mesh.PrimitiveType 列舉。0 = 點,1 = 線段,2 = 線帶,3 = 三角形(最常用),4 = 三角帶。

  • skin_data:骨骼權重資料。

  • vertex_count:表面頂點數,必須與 vertex_data 長度一致。

  • vertex_data:頂點座標資料。

以下為 ArrayMesh 儲存為獨立 .tres 檔案的範例(部分欄位以 ... 省略):

[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

動畫

每個動畫有以下屬性:

  • length:動畫長度(秒)。注意關鍵影格可設於 [0; length] 區間外,但實際效果依所選插值模式而定。

  • loop_mode0 = 不循環,1 = 環繞循環,2 = 限制循環。

  • step:於編輯器編輯此動畫時的步進值。僅供編輯用途,對動畫播放無影響。

每個軌道皆以 tracks/<id>/<attribute> 格式的索引鍵/值組描述。軌道內容包括:

  • type :軌道類型。定義本軌道可動畫化的屬性與編輯器的顯示方式。有效類型包括 value (一般屬性軌道)、 position_3drotation_3dscale_3dblend_shape (最佳化 3D 軌道)、 method (方法軌道)、 bezier (貝塞爾曲線軌道)、 audio (音訊軌道)、 animation (播放其他動畫的軌道)。

  • imported:若軌道來源為匯入 3D 場景則為 true,若由使用者透過 Godot 編輯器或腳本建立則為 false

  • enabled:軌道啟用時為 true,於編輯器中關閉時為 false

  • path:本軌道作用的節點屬性路徑。屬性名稱以 : 接在節點路徑後。

  • interp:插值模式。0 = 最接近,1 = 線性,2 = 立方,3 = 線性角度,4 = 立方角度。

  • loop_wrap:若此軌道設計為動畫循環時自動環繞則為 true,若僅限首/末關鍵影格則為 false

  • keys:本動畫軌道的數值。其結構取決於 type

以下是一個場景,包含一個 AnimationPlayer,利用通用屬性軌道隨時間縮小方塊。此例未使用 AnimationLibrary,因此動畫庫名稱為空(動畫名稱為 scale_down)。為簡潔起見,未建立 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 為字典,含三個陣列:「times」(PackedFloat32Array,關鍵影格時間)、「transitions」(PackedFloat32Array,緩動值)、「values」(Array,數值)。另有一 update 屬性,整數型別:0 = 連續,1 = 離散,2 = 捕獲。

以下為另一個動畫資源,使用 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 位置、旋轉與縮放軌道,keys 為 PackedFloat32Array,所有值依序儲存。

以下說明中,T 代表關鍵影格自動畫起始以來的時間(秒),E 是關鍵影格的轉換值(目前皆為 1)。3D 位置與縮放軌道的 XYZ 為 Vector3 座標值;3D 旋轉軌道的 XYZW 為四元數座標。

# 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, ...)