Формат файла TSCN

Формат файла TSCN (текстовая сцена) представляет собой единое дерево сцен внутри Godot. В отличие от двоичных файлов SCN, файлы TSCN обладают тем преимуществом, что они в основном понятны человеку и просты в управлении системами контроля версий.

Формат файла ESCN (экспортированная сцена) идентичен формату файла TSCN, но используется для указания Godot, что файл был экспортирован из другой программы и не должен редактироваться пользователем в Godot. В отличие от файлов SCN и TSCN, при импорте файлы ESCN компилируются в двоичные файлы SCN, хранящиеся в папке .godot/imported/. Это уменьшает размер данных и ускоряет загрузку, поскольку двоичные форматы загружаются быстрее текстовых.

Для компактности файлов в файлах сцен/ресурсов не сохраняются свойства, равные значениям по умолчанию. Их можно прописать вручную, но при сохранении файла они будут потеряны.

Для тех, кто ищет полное описание, парсинг выполняется в файле resource_format_text.cpp в классе ResourceFormatLoaderText.

Примечание

Форматы файлов сцен и ресурсов существенно изменились в Godot 4 с введением строковых UID, заменивших инкрементные целочисленные идентификаторы.

Данные сетки, скелета и анимации также хранятся иначе по сравнению с 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://cecaux1sm7mo0"] и должен быть первой записью в файле. Параметр load_steps равен общему объёму ресурсов (внутренних и внешних) плюс один (для самого файла). Если в файле нет ресурсов, load_steps опускается. Движок всё равно правильно загрузит файл, даже если load_steps неверен, но это повлияет на загрузку полос и любого другого фрагмента кода, зависящего от этого значения.

uid — это уникальный строковый идентификатор, представляющий сцену. Он используется движком для отслеживания перемещаемых файлов, даже когда редактор закрыт. Скрипты также могут загружать ресурсы на основе UID, используя префикс пути uid://, чтобы не зависеть от путей в файловой системе. Это позволяет перемещать файл в проекте, но при этом загружать его в скрипты без необходимости вносить изменения в сам скрипт. Godot не использует внешние файлы для отслеживания идентификаторов, что означает отсутствие необходимости в централизованном хранилище метаданных в проекте. Подробную информацию см. в этом запросе на извлечение.

Эти разделы должны располагаться по порядку, но их может быть сложно различить. Единственное различие между ними — первый элемент заголовка для всех пунктов раздела. Например, заголовок всех внешних ресурсов должен начинаться с [ext_resource ...].

Файл TSCN может содержать однострочные комментарии, начинающиеся с точки с запятой (;). Однако при сохранении файла в редакторе Godot они будут удалены. Пробелы в файле TSCN не имеют значения (за исключением строк), но лишние пробелы будут удалены при сохранении файла.

Записи внутри файла

Заголовок выглядит как [<resource_type> key1=value1 key2=value2 key3=value3 ...], где resource_type может быть одним из:

  • ext_resource

  • sub_resource

  • node

  • connection

Под каждым заголовком находится ноль или более пар ключ = значение. Значения могут быть сложными типами данных, такими как массивы, преобразования, цвета и т. д. Например, 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)

NоdePath

Древовидной структуры недостаточно для представления всей сцены. Godot использует структуру NodePath(Path/To/Node) для ссылки на другой узел или атрибут узла в любом месте дерева сцены. Пути задаются относительно текущего узла: NodePath(".") указывает на текущий узел, а NodePath("") не указывает ни на какой узел.

Например, MeshInstance3D использует NodePath() для указания на свой скелет. Аналогично, дорожки анимации используют NodePath() для указания на свойства узлов для анимации.

NodePath также может указывать на свойство с помощью суффикса :property_name и даже на конкретный компонент для векторных, преобразованных и цветовых типов. Это используется ресурсами анимации для указания на конкретные свойства для анимации. Например, NodePath("MeshInstance3D:scale.x") указывает на компонент x свойства scale Vector3 в MeshInstance3D.

Например, свойство skeleton в узле MeshInstance3D с именем mesh указывает на его родителя, Armature01:

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

Skeleton3D

Узел Skeleton3D наследует узел Node3D, но может также содержать список костей, описанных парами «key-value» в формате bones/<id>/<attribute> = value. Атрибуты костей включают в себя:

  • position: Vector3

  • rotation: Кватернион

  • scale: 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 = "имя кости", а также свойство для индекса соответствующей кости.

Пример узла 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)

АnimationPlayer

Узел AnimationPlayer работает с одной или несколькими библиотеками анимации, хранящимися в ресурсах AnimationLibrary. Библиотека анимации представляет собой набор отдельных ресурсов Animation, структура которых документирована здесь.

Такое разделение между самими анимациями и анимационными библиотеками было реализовано в Godot 4, чтобы анимацию можно было импортировать отдельно от 3D-сеток, что является распространённой практикой в программах для 3D-анимации. Подробности см. в исходном запросе на извлечение <https://github.com/godotengine/godot/pull/59980>`__.

If the library name is empty, then it acts as 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.

Ресурсы

Ресурсы — это компоненты, из которых состоят узлы. Например, узел MeshInstance3D будет иметь сопутствующий ресурс ArrayMesh. Ресурс ArrayMesh может быть как внутренним, так и внешним по отношению к файлу TSCN.

Ссылки на ресурсы обрабатываются уникальными строковыми идентификаторами в заголовке ресурса. Это отличается от свойства uid, которое также есть у каждого внешнего ресурса (но не у подресурсов).

Для обращения к внешним и внутренним ресурсам используются ExtResource("id") и SubResource("id") соответственно. Поскольку существуют разные методы обращения к внутренним и внешним ресурсам, можно использовать один и тот же идентификатор как для внутреннего, так и для внешнего ресурса.

Например, чтобы сослаться на ресурс [ext_resource type="Material" uid="uid://c4cp0al3ljsjv" path="res://material.tres" id="1_7bt6s"], вы должны использовать ExtResource("1_7bt6s").

Внешние ресурсы

Внешние ресурсы — это ссылки на ресурсы, не содержащиеся в самом файле TSCN. Внешний ресурс состоит из пути, типа, UID (используемого для сопоставления его расположения в файловой системе с уникальным идентификатором) и идентификатора (используемого для ссылки на ресурс в файле сцены).

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 комментарии будут удалены. Пробелы в файле TRES не имеют значения (за исключением строк), но лишние пробелы будут удалены при сохранении файла.

Внутренние ресурсы

Файл TSCN может содержать сетки, материалы и другие данные. Они содержатся в разделе внутренние ресурсы файла. Заголовок внутреннего ресурса выглядит так же, как и заголовки внешних ресурсов, за исключением того, что у него нет пути. Внутренние ресурсы также имеют пары key=value под каждым заголовком. Например, форма капсулы столкновения выглядит следующим образом:

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

Некоторые внутренние ресурсы содержат ссылки на другие внутренние ресурсы (например, сетка, имеющая материал). В этом случае ссылающийся ресурс должен появиться перед ссылкой на него. Это означает, что порядок имеет значение в разделе внутренних ресурсов файла.

ArrayMеsh

ArrayMesh состоит из нескольких поверхностей, содержащихся в массиве _surfaces (обратите внимание на начальное подчеркивание). Данные каждой поверхности хранятся в словаре со следующими ключами:

  • aabb: Вычисленная ограничивающая рамка, выровненная по осям, для видимости.

  • attribute_data: Данные атрибутов вершин, такие как нормали, касательные, цвета вершин, UV1, UV2 и пользовательские данные вершин.

  • bone_aabbs: Выровненный по осям ограничивающий прямоугольник каждой кости для видимости.

  • format: Формат буфера поверхности.

  • index_count: Количество индексов на поверхности. Должна соответствовать размеру index_data.

  • index_data: Индексные данные, которые определяют, какие вершины из vertex_data будут отрисованы.

  • lods: уровни детализации, хранящиеся в виде массива. Каждый уровень детализации представлен двумя значениями в массиве. Первое значение — это процент экранного пространства, для которого уровень детализации наиболее подходит (длина ребра); второе значение — это список индексов, которые должны быть отображены для данного уровня детализации.

  • 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_mode: 0 = без цикла, 1 = цикл с обертыванием, 2 = фиксированный цикл.

  • step: Размер шага, используемый при редактировании анимации в редакторе. Используется только в редакторе и никак не влияет на воспроизведение анимации.

Каждый трек описывается списком пар "ключ-значение" в формате tracks/<id>/<attribute>. Каждый трек включает в себя:

  • type: Тип дорожки. Определяет, какие свойства может анимировать эта дорожка, и как они будут отображаться пользователю в редакторе. Допустимые типы: value (дорожка общих свойств), position_3d, rotation_3d, scale_3d, blend_shape (дорожки оптимизированной 3D-анимации), method (дорожки вызова методов), bezier (дорожки кривых Bezier), audio (дорожки воспроизведения звука), animation (дорожки, воспроизводящие другие анимации).

  • imported: true, если трек был создан из импортированной 3D-сцены, false, если он был создан пользователем вручную в редакторе Godot или с помощью скрипта.

  • enabled: true, если трек эффективен, false, если он был отключен в редакторе.

  • path: Путь к свойству узла, на которое будет влиять трек. Свойство указывается после пути к узлу с разделителем :.

  • interp: Режим интерполяции, который нужно использовать. 0 = ближайший, 1 = линейный, 2 = кубический, 3 = линейный угол, 4 = кубический угол.

  • loop_wrap: true, если дорожка предназначена для циклического воспроизведения анимации, false, если дорожка ограничивается первым/последним ключевыми кадрами.

  • keys: Значения анимационной дорожки. Структура этого атрибута зависит от type.

Вот сцена с AnimationPlayer, который уменьшает куб с течением времени, используя общую дорожку свойств. Рабочий процесс AnimationLibrary не использовался, поэтому библиотека анимации имеет пустое имя (но анимации по-прежнему присвоено имя scale_down). Обратите внимание, что дорожка RESET в этом AnimationPlayer не создана для краткости:

[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 массива: позиции в times (PackedFloat32Array), значения плавности в transitions (PackedFloat32Array) и значения в values (Array). Имеется дополнительное свойство update, представляющее собой целое число со значениями 0 = непрерывный, 1 = дискретный, 2 = захват.

Вот второй ресурс Animation, использующий дорожки 3D-положения и 3D-вращения. Эти дорожки (в дополнение к дорожке 3D-масштаба) заменяют дорожки преобразования из Godot 3. Они оптимизированы для быстрого воспроизведения и могут быть сжаты при необходимости.

Недостатком этих оптимизированных типов треков является невозможность использования настраиваемых значений замедления. Вместо этого все ключевые кадры используют линейную интерполяцию. Тем не менее, вы по-прежнему можете выбрать ближайшую или кубическую интерполяцию для всех ключевых кадров на данном треке, изменив режим интерполяции трека.

[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 представляет собой упакованный массив Float32Array, в котором все значения хранятся в последовательности.

В визуальном руководстве ниже T — это время ключевого кадра в секундах с момента начала анимации, E — переход ключевого кадра (в настоящее время всегда 1). Для треков 3D-положения и масштаба X, Y, Z — координаты Vector3. Для треков 3D-вращения X, Y, Z и W — координаты Quaternion.

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