Разработка игрДругой язык

Как сделать первую 2D-игру в Godot 4 на GDScript

Создаём 2D-сцену без внешних ассетов: персонаж двигается, сталкивается с монетой, сигнал увеличивает счёт, а Godot показывает связь узлов и GDScript.

Кодик

Автор

6 мин чтения

Первая игра в Godot становится понятной, когда каждый узел отвечает за одну роль: игрок двигается, монета замечает столкновение, а главная сцена считает очки. Соберём проект без скачивания ассетов. Персонаж и монета рисуются кодом, стрелки двигают CharacterBody2D, Area2D испускает сигнал, а Label показывает счёт.

Игровой движок соединяет сцену и код. Сцена хранит дерево объектов, свойства и связи, а GDScript описывает поведение. Если начать с готового большого шаблона, легко потерять эту связь. Поэтому у нас всего четыре заметных узла: Main, Player, Coin и Score. У Player есть CollisionShape2D и скрипт движения.

1Двигаем

Input.get_vector превращает четыре действия клавиатуры в нормализованное направление.

2Собираем

Area2D замечает body_entered, испускает collected и удаляет монету.

3Считаем

Главная сцена получает сигнал, увеличивает 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 обновилась
PhysicsCharacterBody2D вызывает move_and_slideИгрок изменил position
CollisionArea2D получает body_enteredМонета узнала Player
Signalcollected приходит в MainСчёт вырос, монета исчезла

У проекта есть один главный маршрут: получить входные данные, проверить их, выполнить действие и показать результат. Если каждый этап можно проверить отдельно, ошибка перестаёт быть загадкой.

Первый игровой цикл Godot
От входных данных до видимого результата. Ввод превращается в состояние через движение и столкновение

Сначала проверьте дерево сцены, потом код. Неверное имя узла или отсутствующий CollisionShape2D не исправляются формулой движения. Сверьте типы и вложенность Main/Player/Coin/Score до запуска.

Создаём игрока, монету и движение

Установите стабильный 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 loopvelocity задаётся каждый tick
CollisionShape2DДаёт форму физикеФорма не disabled
Area2D signalСообщает о входе телаСигнал подключён один раз
Main scriptХранит score и обновляет UICoin не ищет Label напрямую

Input.get_vector объединяет четыре действия и ограничивает длину вектора, поэтому диагональ не ускоряет игрока примерно в 1.41 раза. `_physics_process` вызывается с фиксированной частотой для физической логики. В нашем коде _delta не используется, потому что velocity задаётся как скорость, а move_and_slide обрабатывает шаг.

Нарисованный круг и collision shape являются разными вещами. Если объект виден, но столкновения нет, проверьте CollisionShape2D и collision layers/masks. Если форма есть, но _draw пуст, физика может работать с невидимым объектом.

Проверяем границы, счёт и повторный запуск

Запустите игру и проверьте движение по горизонтали, вертикали и диагонали. Подойдите к каждой границе окна. Затем временно отключите shape монеты и убедитесь, что визуальный круг остаётся, а collected не приходит. Верните форму. Измените позицию Coin в main.tscn и повторите сбор.

Проверка своими руками
  1. Проверьте четыре направления и нормальную диагональную скорость.
  2. Дойдите до всех границ и убедитесь, что Player не исчезает.
  3. Отключите CollisionShape2D монеты и объясните результат.
  4. Верните форму и соберите монету ровно один раз.
  5. Переместите Coin в сцене без изменения кода.
  6. Добавьте вторую монету или условие победы при score=3.
Готово, если выполняются все пункты
  • Игрок двигается одинаково по осям и диагонали.
  • Границы окна работают.
  • Сигнал увеличивает счёт один раз.
  • Роли Player, Coin и Main не смешаны.

Первая игра уже содержит полный игровой цикл: ввод, движение, столкновение, событие, изменение состояния и обратную связь. Новая механика должна встраиваться в этот цикл, а не превращать один скрипт в свалку.

Четыре проверки 2D-прототипа
Четыре проверки перед следующим шагом. Каждая механика проверяется отдельно в сцене

Развиваем прототип в настоящую игру

Следующим шагом создайте PackedScene для монеты и спавните новый экземпляр после сбора. Затем добавьте Timer, ограничение времени и экран перезапуска. Для стен используйте StaticBody2D, для анимации AnimatedSprite2D, для звука AudioStreamPlayer. Не добавляйте всё сразу: после каждого узла запускайте сцену и проверяйте один сценарий.

Как изучить тему в Кодике

В Кодике сначала закрепите переменные, функции, условия и векторы. Затем разберите сигнал как сообщение между объектами. После этого дерево Godot читается как набор знакомых ролей, а не набор специальных названий.

ШагЧто изучить в КодикеМини-проверка
1Переменные и функцииИзменить speed и score
2Vector2 и направлениеПолучить движение из четырёх действий
3Узлы и сценыОбъяснить роли четырёх узлов
4SignalsПередать collected из Coin в Main
5Игровой циклДобавить вторую монету и условие победы

Не пытайтесь запомнить весь API. В тренажёре Кодика сначала воспроизведите чистую логику без библиотеки, затем восстановите подключение и обработку ошибок. Финальный тест: объяснить проект по памяти и добавить одну свою функцию.

Диагональ собирается через два if

Используйте Input.get_vector или нормализуйте направление, иначе скорость будет выше.

Коллизия ожидается от _draw

Рисование не создаёт physics shape. Добавьте CollisionShape2D.

Coin напрямую меняет Label

Пусть монета испускает сигнал, а Main управляет счётом и интерфейсом.

velocity умножают на delta перед move_and_slide

Для CharacterBody2D задавайте скорость и вызывайте move_and_slide по модели Godot 4.

Сверьтесь с первичным источником. Команды, версии и ограничения примера проверяйте по официальной документации Godot: Your first 2D game. Если интерфейс изменился, первичная документация важнее старого скриншота.

Что получится в итоге

Готовый результат: Как сделать первую 2D-игру в Godot 4 на GDScript
Игрок двигается стрелками, собирает монету через Area2D signal, а главная сцена увеличивает счёт и показывает первый законченный игровой цикл.

Короткие ответы
Почему диагональ не быстрее?

Input.get_vector ограничивает длину направления, поэтому скорость остаётся одинаковой.

Зачем Coin испускает свой сигнал?

Он сообщает факт сбора, не зная, как Main хранит счёт и рисует интерфейс.

Создаёт ли _draw столкновение?

Нет. Вид задаёт рисование, а физическую форму создаёт CollisionShape2D.

Как понять, что проект действительно работает?

Проверьте основной сценарий, неверный ввод, повторный запуск и один граничный случай. Затем объясните путь данных своими словами.

Можно ли начать с готового кода из статьи?

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

Закрепите один полный игровой цикл, затем добавляйте механики

Основы переменных, условий и функций закрепите в курсе Lua и игровой логики. Событийную модель сравните со статьёй про spawn в Roblox Studio.

Для визуального движения посмотрите анимацию, а полный путь публикации игрового проекта сравните с материалом про публикацию игры