Генерация уровня и расстановка объектов
Расставлять сотни монет вручную утомительно — гораздо удобнее создавать объекты кодом по правилам.
Суть: загружаешь сцену-заготовку, в цикле делаешь её копии (instantiate) и добавляешь в дерево (add_child) на нужные позиции.
Когда уровень большой, ставить каждую монетку мышкой невыносимо. Программная генерация решает это: ты делаешь одну сцену-заготовку (монетку, врага, платформу) и кодом создаёшь сколько угодно её копий, раскладывая по сетке или случайно. Создание копии сцены называется инстансированием: preload загружает заготовку, instantiate() делает её свежую копию, add_child() добавляет копию в дерево, чтобы она появилась в игре.
extends Node2D
@export var coin_scene: PackedScene
@export var rows: int = 3
@export var cols: int = 5
@export var spacing: float = 64.0
func _ready() -> void:
for row in range(rows):
for col in range(cols):
var coin := coin_scene.instantiate()
coin.position = Vector2(col * spacing, row * spacing)
add_child(coin)Эти три строки внутри двойного цикла раскладывают аккуратную сетку монет. Меняешь rows и cols в инспекторе — меняется уровень, и ни одну монетку не нужно ставить вручную.
Полезно увидеть здесь дверь к процедурной генерации — приёму, на котором держатся целые жанры. Если вместо ровной сетки подставлять случайные позиции, разное число объектов и разные типы заготовок, каждый запуск игры будет рождать новый, неповторимый уровень. Так устроены roguelike-игры и бесконечные раннеры: контент создаётся кодом на лету, а не рисуется вручную. Начав с простой сетки монет, ты уже владеешь базовым кирпичом этого подхода — цикл, заготовка, instantiate, add_child. Дальше всё упирается лишь в то, по каким правилам выбирать позиции и что именно порождать.
Как работает под капотом
Сцена-заготовка хранится как PackedScene — упакованный шаблон. instantiate() разворачивает шаблон в новый набор узлов (свежую копию со своими переменными). add_child() вставляет корень копии в дерево как потомка, после чего движок начинает её обновлять и рисовать. Цикл просто повторяет это нужное число раз, подставляя разные позиции.
spacing = 64
rows, cols = 3, 5
coins = []
for row in range(rows):
for col in range(cols):
pos = (col * spacing, row * spacing)
coins.append(pos) # "add_child" копии монеты
print("Создано монет:", len(coins))
print("Первые три позиции:", coins[:3])
print("Последняя позиция: ", coins[-1])Та же логика на Python ▶. Двойной цикл по строкам и столбцам считает позицию каждой монеты как (col * spacing, row * spacing). В Godot к каждой такой позиции добавляется реальная копия сцены через instantiate и add_child.
Стоит заранее подумать об уборке за собой. Если объекты не только создаются, но и должны исчезать (собранные монеты, убитые враги, улетевшие за экран пули), важно вовремя звать queue_free для ненужных копий. Иначе они тихо копятся в дереве, едят память и потихоньку роняют FPS — особенно в бесконечных или длинных уровнях. Хорошее правило: на каждый instantiate подумай, когда и как этот объект покинет игру. Аккуратное создание вместе с аккуратным удалением держит сцену лёгкой и быструю даже после долгой игры, и это отличает крепкий проект от того, что тормозит через пять минут.
Частые ошибки
Первая ошибка — забыть add_child после instantiate: копия создана, но её нет в дереве, поэтому она невидима и неактивна. Создал — добавь. Вторая — переиспользовать один и тот же экземпляр вместо новых копий: тогда «монеты» окажутся одним объектом, прыгающим по позициям. Каждой монете — свой instantiate(). Третья — генерировать тысячи объектов без меры: уровень начнёт тормозить; ограничивай количество. Четвёртая — путать preload (на этапе загрузки) и load (во время игры) для тяжёлых сцен.
Best practices
Делай объект один раз как отдельную сцену, потом порождай копии кодом. Параметры генерации (число рядов, отступ, плотность) выноси в @export, чтобы перестраивать уровень из инспектора. Для каждой новой штуки зови instantiate() и обязательно add_child(). Считай позиции через индексы цикла и шаг (col * spacing). Ограничивай количество объектов, чтобы держать стабильный FPS.
Итоги: объекты уровня удобно создавать кодом: preload заготовки, instantiate() копии, add_child() в дерево. Двойной цикл по строкам и столбцам раскладывает сетку, позиция = индекс * шаг. Параметры генерации выноси в @export. Каждой копии — свой instantiate, всегда добавляй её в дерево и ограничивай число объектов.