Сцены и дерево узлов

Сцена — это группа узлов, собранных в дерево: один главный узел сверху и потомки под ним.

Суть: узлы не лежат кучей, а выстраиваются в дерево «родитель → потомки», и это дерево и есть твоя сцена.

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

Группа узлов в дереве, сохранённая в отдельный файл, называется сценой (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. Группируй связанные узлы под общим родителем, чтобы их можно было двигать и прятать одним движением.

Итоги: узлы выстраиваются в дерево «родитель → потомки», потомок едет вместе с родителем. Дерево, сохранённое в файл, — это сцена, и сцены вкладываются друг в друга. У сцены ровно один корень. Обход дерева сверху вниз — обычная рекурсия, и именно так движок рисует кадр.

Проверьте себя
1. Что происходит с потомками узла, когда родитель сдвигается?
AНичего, потомки остаются на месте
BПотомки двигаются вместе с родителем
CПотомки удаляются из дерева
DИгра вылетает
2. Что такое сцена в Godot?
AОдин отдельный узел
BГруппа узлов в дереве, сохранённая в файл
CКадр игрового цикла
DКартинка фона