Сцены и дерево узлов
Сцена — это группа узлов, собранных в дерево: один главный узел сверху и потомки под ним.
Суть: узлы не лежат кучей, а выстраиваются в дерево «родитель → потомки», и это дерево и есть твоя сцена.
Один узел — это мало для игры. Персонаж — это не просто картинка: ему нужна форма для столкновений, может быть, анимация, область видимости. Чтобы собрать это вместе, узлы складывают в дерево. У дерева есть корень — самый верхний узел, и потомки — узлы под ним. Потомок наследует позицию родителя: если родитель сдвинулся, все его потомки едут вместе с ним. Это очень удобно: двигаешь персонажа — и его меч, щит и полоска здоровья едут следом сами.
Группа узлов в дереве, сохранённая в отдельный файл, называется сценой (Scene). Сцена — это законченный кусок игры, который можно переиспользовать. Монетка — сцена. Враг — сцена. Главное меню — сцена. Целый игровой уровень — тоже сцена, в которую вложены сцены монеток и врагов. Сцены вкладываются друг в друга, как матрёшки.
Вот как может выглядеть дерево сцены персонажа:
Player (CharacterBody2D) <- корень сцены ├── Sprite2D <- картинка персонажа ├── CollisionShape2D <- форма для столкновений └── Camera2D <- камера едет за игроком
Здесь Player — корень. Когда Player двигается, Sprite2D, CollisionShape2D и Camera2D двигаются вместе с ним, потому что они его потомки. Это и есть магия дерева.
Ещё одно следствие древовидной структуры — переиспользование. Сделав сцену врага один раз, ты можешь поставить на уровень хоть сотню его копий, и все они будут вести себя одинаково, потому что собраны из одного шаблона. Поправил шаблон — изменились все копии разом. Это экономит уйму времени: тебе не нужно настраивать каждого врага отдельно. Большие игры именно так и устроены: маленькие, хорошо отлаженные сцены складываются во всё более крупные, пока не получится целый уровень или даже вся игра. Думай о сценах как о деталях лего, которые ты однажды отлил и теперь свободно собираешь.
Как работает под капотом
Дерево узлов — это классическая структура данных: у каждого узла есть ссылка на родителя и список потомков. Когда движок отрисовывает кадр, он обходит дерево сверху вниз: сначала родитель, потом его потомки. Позиция потомка считается относительно родителя, поэтому сдвиг родителя автоматически сдвигает всю ветку.
Смоделируем дерево узлов на Python. Каждый узел знает своих потомков; функция, которая печатает дерево с отступами, обходит его рекурсивно — как делает и сам движок.
class Node:
def __init__(self, name):
self.name = name
self.children = []
def add_child(self, node):
self.children.append(node)
def print_tree(node, depth=0):
print(" " * depth + node.name)
for child in node.children:
print_tree(child, depth + 1)
player = Node("Player")
player.add_child(Node("Sprite2D"))
player.add_child(Node("CollisionShape2D"))
player.add_child(Node("Camera2D"))
print_tree(player)
print("Потомков у Player:", len(player.children))Та же логика на Python ▶. Дерево — это узлы со списком потомков, а обход сверху вниз — обычная рекурсия. Godot делает ровно это под капотом каждый кадр.
Стоит добавить про корень сцены: именно он определяет «тип» всей сцены и то, как она ведёт себя, когда её вставляют в другую сцену. Если корень — Area2D, вся сцена работает как зона-триггер; если CharacterBody2D — как управляемый персонаж. Поэтому выбор корня — не мелочь, а проектное решение. Хорошее правило: спроси себя, чем эта сцена является в первую очередь, и сделай корнем узел, отражающий эту суть. Остальные узлы станут его потомками и будут обслуживать главную роль корня.
Частые ошибки
Новички часто кладут все узлы плоско, без вложенности, и потом удивляются, почему меч не едет за персонажем. Если хочешь, чтобы объект двигался вместе с другим, сделай его потомком. Вторая ошибка — путать удаление узла и его скрытие: если убрать узел из дерева, он перестаёт обновляться и рисоваться; если просто спрятать (visible = false), он остаётся в дереве и продолжает жить. Третья — забывать, что у сцены ровно один корень: нельзя иметь два главных узла на одном уровне сверху.
Best practices
Делай маленькие самостоятельные сцены и собирай из них большие. Сцену монетки делай один раз, а потом расставляй её копии по уровню. Корнем сцены выбирай узел, который описывает её суть: для персонажа — CharacterBody2D, для области-триггера — Area2D. Группируй связанные узлы под общим родителем, чтобы их можно было двигать и прятать одним движением.
Итоги: узлы выстраиваются в дерево «родитель → потомки», потомок едет вместе с родителем. Дерево, сохранённое в файл, — это сцена, и сцены вкладываются друг в друга. У сцены ровно один корень. Обход дерева сверху вниз — обычная рекурсия, и именно так движок рисует кадр.