Первая игра в Godot становится понятной, когда каждый узел отвечает за одну роль: игрок двигается, монета замечает столкновение, а главная сцена считает очки. Соберём проект без скачивания ассетов. Персонаж и монета рисуются кодом, стрелки двигают CharacterBody2D, Area2D испускает сигнал, а Label показывает счёт.
Игровой движок соединяет сцену и код. Сцена хранит дерево объектов, свойства и связи, а GDScript описывает поведение. Если начать с готового большого шаблона, легко потерять эту связь. Поэтому у нас всего четыре заметных узла: Main, Player, Coin и Score. У Player есть CollisionShape2D и скрипт движения.
Input.get_vector превращает четыре действия клавиатуры в нормализованное направление.
Area2D замечает body_entered, испускает collected и удаляет монету.
Главная сцена получает сигнал, увеличивает score и обновляет Label.
Как сцена Godot связывает узлы и скрипты
main.tscn создаёт Node2D Main, дочерний Player типа CharacterBody2D, Coin типа Area2D и Label. У двух игровых объектов есть CircleShape2D. player.gd каждый physics tick читает ui_left, ui_right, ui_up и ui_down, задаёт velocity и вызывает move_and_slide. Затем позиция ограничивается размером окна.
| Этап | Что происходит | Признак результата |
|---|---|---|
| Input | Стрелки превращаются в Vector2 направления | velocity обновилась |
| Physics | CharacterBody2D вызывает move_and_slide | Игрок изменил position |
| Collision | Area2D получает body_entered | Монета узнала Player |
| Signal | collected приходит в Main | Счёт вырос, монета исчезла |
У проекта есть один главный маршрут: получить входные данные, проверить их, выполнить действие и показать результат. Если каждый этап можно проверить отдельно, ошибка перестаёт быть загадкой.

От входных данных до видимого результата. Ввод превращается в состояние через движение и столкновение
Создаём игрока, монету и движение
Установите стабильный Godot 4, создайте новый проект и добавьте файлы из исходников статьи. Укажите main.tscn как Main Scene. Встроенные действия ui_left, ui_right, ui_up и ui_down уже связаны со стрелками. Запустите F6 для сцены или F5 для проекта. Если редактор импортировал файлы, персонаж появится слева, монета справа, а счёт сверху.
extends CharacterBody2D
@export var speed := 260.0
func _ready() -> void:
queue_redraw()
func _draw() -> void:
draw_circle(Vector2.ZERO, 28.0, Color("3478f6"))
func _physics_process(_delta: float) -> void:
var direction := Input.get_vector(
"ui_left", "ui_right", "ui_up", "ui_down"
)
velocity = direction * speed
move_and_slide()
var size := get_viewport_rect().size
position.x = clamp(position.x, 28.0, size.x - 28.0)
position.y = clamp(position.y, 28.0, size.y - 28.0)- Синий круг появляется в позиции Player и двигается стрелками.
- Диагональное движение не становится быстрее благодаря Input.get_vector.
- Игрок не выходит за границы окна после clamp.
- При касании жёлтой монеты счёт становится 1, а Coin удаляется.
Скорость задаётся в пикселях в секунду, а CharacterBody2D работает в physics callback. Для move_and_slide не нужно умножать velocity на delta вручную: метод и тип тела уже рассчитаны на эту модель движения.

Что отличает устойчивый проект от случайного успеха. Сигнал позволяет объектам не знать лишнее друг о друге
Почему столкновение передаётся через сигнал
CharacterBody2D участвует в физическом мире как движущееся тело. CollisionShape2D задаёт форму для пересечений, а _draw только показывает цветной круг и не создаёт коллизию. Area2D не толкает игрока, но отслеживает вход тела. Сигнал body_entered вызывается движком, coin.gd переводит его в предметный сигнал collected, а Main решает, что делать со счётом.
| Часть | Ответственность | Что проверить |
|---|---|---|
| Scene tree | Хранит роли и вложенность узлов | Имена совпадают с путями $Node |
| CharacterBody2D | Двигает игрока в physics loop | velocity задаётся каждый tick |
| CollisionShape2D | Даёт форму физике | Форма не disabled |
| Area2D signal | Сообщает о входе тела | Сигнал подключён один раз |
| Main script | Хранит score и обновляет UI | Coin не ищет Label напрямую |
Input.get_vector объединяет четыре действия и ограничивает длину вектора, поэтому диагональ не ускоряет игрока примерно в 1.41 раза. `_physics_process` вызывается с фиксированной частотой для физической логики. В нашем коде _delta не используется, потому что velocity задаётся как скорость, а move_and_slide обрабатывает шаг.
Проверяем границы, счёт и повторный запуск
Запустите игру и проверьте движение по горизонтали, вертикали и диагонали. Подойдите к каждой границе окна. Затем временно отключите shape монеты и убедитесь, что визуальный круг остаётся, а collected не приходит. Верните форму. Измените позицию Coin в main.tscn и повторите сбор.
- Проверьте четыре направления и нормальную диагональную скорость.
- Дойдите до всех границ и убедитесь, что Player не исчезает.
- Отключите CollisionShape2D монеты и объясните результат.
- Верните форму и соберите монету ровно один раз.
- Переместите Coin в сцене без изменения кода.
- Добавьте вторую монету или условие победы при score=3.
- Игрок двигается одинаково по осям и диагонали.
- Границы окна работают.
- Сигнал увеличивает счёт один раз.
- Роли Player, Coin и Main не смешаны.
Первая игра уже содержит полный игровой цикл: ввод, движение, столкновение, событие, изменение состояния и обратную связь. Новая механика должна встраиваться в этот цикл, а не превращать один скрипт в свалку.

Четыре проверки перед следующим шагом. Каждая механика проверяется отдельно в сцене
Развиваем прототип в настоящую игру
Следующим шагом создайте PackedScene для монеты и спавните новый экземпляр после сбора. Затем добавьте Timer, ограничение времени и экран перезапуска. Для стен используйте StaticBody2D, для анимации AnimatedSprite2D, для звука AudioStreamPlayer. Не добавляйте всё сразу: после каждого узла запускайте сцену и проверяйте один сценарий.
Как изучить тему в Кодике
В Кодике сначала закрепите переменные, функции, условия и векторы. Затем разберите сигнал как сообщение между объектами. После этого дерево Godot читается как набор знакомых ролей, а не набор специальных названий.
| Шаг | Что изучить в Кодике | Мини-проверка |
|---|---|---|
| 1 | Переменные и функции | Изменить speed и score |
| 2 | Vector2 и направление | Получить движение из четырёх действий |
| 3 | Узлы и сцены | Объяснить роли четырёх узлов |
| 4 | Signals | Передать collected из Coin в Main |
| 5 | Игровой цикл | Добавить вторую монету и условие победы |
Не пытайтесь запомнить весь API. В тренажёре Кодика сначала воспроизведите чистую логику без библиотеки, затем восстановите подключение и обработку ошибок. Финальный тест: объяснить проект по памяти и добавить одну свою функцию.
Используйте Input.get_vector или нормализуйте направление, иначе скорость будет выше.
Рисование не создаёт physics shape. Добавьте CollisionShape2D.
Пусть монета испускает сигнал, а Main управляет счётом и интерфейсом.
Для CharacterBody2D задавайте скорость и вызывайте move_and_slide по модели Godot 4.
Что получится в итоге

Игрок двигается стрелками, собирает монету через Area2D signal, а главная сцена увеличивает счёт и показывает первый законченный игровой цикл.
Почему диагональ не быстрее?
Input.get_vector ограничивает длину направления, поэтому скорость остаётся одинаковой.
Зачем Coin испускает свой сигнал?
Он сообщает факт сбора, не зная, как Main хранит счёт и рисует интерфейс.
Создаёт ли _draw столкновение?
Нет. Вид задаёт рисование, а физическую форму создаёт CollisionShape2D.
Как понять, что проект действительно работает?
Проверьте основной сценарий, неверный ввод, повторный запуск и один граничный случай. Затем объясните путь данных своими словами.
Можно ли начать с готового кода из статьи?
Да. Добейтесь результата, измените одно правило и соберите ключевой файл заново без копирования.
Основы переменных, условий и функций закрепите в курсе Lua и игровой логики. Событийную модель сравните со статьёй про spawn в Roblox Studio.
Для визуального движения посмотрите анимацию, а полный путь публикации игрового проекта сравните с материалом про публикацию игры