Основы Godot: Создание гибрида Plants vs. Zombies и Blox Fruits

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

1. Узлы и сцены в Godot

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

Что такое узел (Node)

Узел (Node) — это мельчайший строительный блок в Godot. Каждый узел выполняет одну конкретную, узконаправленную задачу.

Представьте себе конструктор Lego. Отдельная деталь — это узел. У вас есть деталь-колесо, деталь-мотор, деталь-руль и деталь-фара. Сами по себе они не являются машиной, но каждая из них имеет свою четкую функцию.

В разработке игр на Godot принцип абсолютно такой же. Движок предоставляет сотни готовых узлов, каждый из которых отвечает за свою часть работы:

* Один узел умеет рисовать картинку на экране. * Другой узел умеет проигрывать звуковой эффект. * Третий узел работает как невидимый таймер, отсчитывающий секунды. * Четвертый узел определяет физические границы объекта, чтобы он мог сталкиваться с другими.

> Узел — это базовый элемент игры, обладающий определенными свойствами (например, позицией на экране) и способный выполнять специфические действия.

Для создания гибрида Plants vs. Zombies и Blox Fruits вам понадобятся узлы, которые будут отвечать за отображение магических фруктов-защитников, отслеживание врагов на линии и расчет времени перезарядки способностей.

Основные типы узлов для 2D-игры

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

| Название узла | Зачем нужен в игре | Пример использования в вашем проекте | | :--- | :--- | :--- | | Sprite2D | Отображает 2D-изображение (текстуру). | Картинка магического фрукта или анимация идущего врага. | | Area2D | Обнаруживает пересечения с другими объектами. | Зона видимости защитника: как только враг входит в эту зону, фрукт начинает атаковать. | | CollisionShape2D | Задает физическую форму (круг, прямоугольник) для столкновений. | Хитбокс врага, по которому должен попасть магический снаряд. | | Timer | Отсчитывает время и подает сигнал по завершении. | Перезарядка атаки: фрукт стреляет огненным шаром строго раз в 1.5 секунды. | | CharacterBody2D | Узел для объектов, которыми управляет физика или игрок. | Враг, который движется по линии и останавливается, если упирается в защитника. |

Что такое сцена (Scene)

Если узел — это отдельная деталь Lego, то сцена (Scene) — это собранная из этих деталей готовая конструкция. Например, собранный из колес, мотора и кабины автомобиль.

Сцена в Godot — это просто набор узлов, организованных в определенном порядке и сохраненных в один файл (с расширением .tscn). Сцена представляет собой самостоятельный игровой объект.

В вашей игре сценами будут:

  • Отдельный магический фрукт (защитник).
  • Отдельный враг.
  • Магический снаряд (например, огненный шар).
  • Главный уровень, на котором расположена сетка для посадки фруктов.
  • Прелесть Godot заключается в том, что сцены могут содержать в себе другие сцены. Ваш главный уровень — это большая сцена, внутрь которой вы будете помещать сцены защитников и врагов.

    Иерархия: Родительские и дочерние узлы

    Узлы внутри сцены не просто свалены в кучу. Они организованы в строгую древовидную структуру, которая называется деревом сцен (Scene Tree). В этом дереве всегда есть один главный узел (корневой), а все остальные подчиняются ему.

    Здесь возникает концепция родительских (Parent) и дочерних (Child) узлов.

    Правило иерархии очень простое: дочерние узлы наследуют трансформацию (позицию, вращение и масштаб) своего родителя.

    Если родительский узел перемещается в координаты и , то все его дочерние узлы автоматически переместятся вместе с ним. Это критически важно для разработки. Когда ваш враг идет по линии, вам не нужно отдельно двигать его картинку (Sprite2D), его хитбокс (CollisionShape2D) и его полоску здоровья. Вы двигаете только родительский узел, а все остальные элементы, будучи дочерними, послушно следуют за ним.

    !Схема дерева сцен в Godot

    Экземпляры сцен (Instancing)

    Представьте, что игрок накопил достаточно энергии и хочет поставить на поле сразу пять одинаковых огненных фруктов. Вам не нужно пять раз собирать узлы Sprite2D, Area2D и Timer с нуля.

    Вместо этого вы создаете экземпляр (Instance) сцены.

    Экземпляр — это копия сохраненной сцены, помещенная в другую сцену. Вы один раз создаете идеального защитника, настраиваете его урон, радиус атаки и картинку, сохраняете как сцену fire_fruit.tscn, а затем просто создаете ее экземпляры на игровом поле столько раз, сколько захочет игрок.

    Главное преимущество экземпляров — централизованное управление. Если в процессе тестирования игры вы поймете, что огненный фрукт наносит слишком мало урона, вам не придется менять настройки у каждого из пятидесяти фруктов на уровне. Вы открываете оригинальную сцену fire_fruit.tscn, увеличиваете урон там, и все экземпляры в игре автоматически обновляются.

    Практический пример: Создание базового защитника

    Давайте рассмотрим, как концепция узлов и сцен применяется на практике при создании вашего первого защитника — магического фрукта, который будет стоять на клетке и атаковать врагов.

  • Создание корневого узла. Вы создаете новую сцену и выбираете Area2D в качестве корневого узла. Этот узел идеально подходит для защитника, так как он не должен двигаться под воздействием гравитации, но должен уметь обнаруживать врагов, входящих в его зону поражения. Назовем этот узел MagicDefender.
  • Добавление визуальной части. К узлу MagicDefender вы добавляете дочерний узел Sprite2D. В его настройках вы выбираете текстуру (картинку) вашего фрукта. Теперь защитника видно на экране.
  • Настройка физических границ. Чтобы Area2D понимал, где именно находится фрукт и какие у него размеры, вы добавляете еще один дочерний узел — CollisionShape2D. В его настройках вы выбираете форму (например, прямоугольник) и растягиваете ее так, чтобы она покрывала картинку фрукта.
  • Добавление логики атаки. Чтобы фрукт атаковал не непрерывно, а с определенной задержкой, вы добавляете дочерний узел Timer. В его настройках выставляете время (например, 2 секунды) и включаете автозапуск.
  • В результате у вас получается небольшое дерево узлов:

    MagicDefender (Area2D) — корневой узел, отвечает за позицию на сетке и обнаружение врагов* Sprite2Dдочерний узел, отвечает за красоту* CollisionShape2Dдочерний узел, отвечает за физические границы* Timerдочерний узел, отвечает за ритм стрельбы*

    Вы сохраняете эту конструкцию. Теперь это полноценная сцена.

    Чтобы заставить эту сцену работать, к корневому узлу прикрепляется скрипт — текстовый файл с кодом на языке GDScript. Скрипт позволяет узлам общаться между собой. Например, когда узел Timer заканчивает отсчет, он подает сигнал скрипту, а скрипт отдает команду создать экземпляр огненного шара и запустить его во врага.

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

    2. Базовые скрипты на GDScript

    В предыдущей статье мы выяснили, что игра строится из узлов (мельчайших деталей) и сцен (готовых конструкций). Вы создали сцену MagicDefender, добавили ей картинку, физические границы и таймер. Однако сейчас ваш магический фрукт — это просто красивая, но абсолютно безжизненная картинка на экране. Он не знает, сколько у него здоровья, не умеет стрелять и не понимает, что делать, когда таймер заканчивает отсчет.

    Чтобы вдохнуть жизнь в узлы, им нужен «мозг». В Godot роль этого мозга выполняют скрипты, написанные на языке GDScript.

    Что такое GDScript

    GDScript — это встроенный язык программирования движка Godot. Он был создан специально для разработки игр, поэтому в нем нет лишних усложнений, которые встречаются в языках общего назначения (вроде C++ или Java). Внешне и по своей логике он очень похож на Python: он читается почти как обычный английский текст, использует отступы для структурирования кода и прощает новичкам многие ошибки.

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

    Когда вы прикрепляете скрипт к корневому узлу вашей сцены (например, к Area2D вашего защитника), этот скрипт получает полный контроль над самим узлом и всеми его дочерними элементами.

    !Схема взаимодействия скрипта и узла в Godot

    Переменные: Хранилище данных

    Любой игре нужно запоминать информацию. Сколько здоровья осталось у базы? Какой урон наносит огненный шар? Сколько энергии накопил игрок для посадки нового фрукта?

    Для хранения этой информации используются переменные. Представьте переменную как коробку с наклейкой. На наклейке написано имя коробки, а внутрь вы кладете какое-то значение.

    В GDScript переменные создаются с помощью ключевого слова var:

    В этом примере мы создали четыре «коробки»:

  • health (здоровье) хранит целое число 100.
  • damage (урон) хранит целое число 25.
  • fruit_name (имя фрукта) хранит текст (строку).
  • is_alive (жив ли объект) хранит логическое значение: true (истина) или false (ложь).
  • Если значение никогда не должно меняться в процессе игры (например, максимальное здоровье базы), вместо var используется const (константа):

    Функции: Действия и поведение

    Если переменные — это существительные (что мы имеем), то функции — это глаголы (что мы делаем). Функция — это блок кода, который выполняет конкретную задачу.

    В Godot есть встроенные функции, которые движок вызывает автоматически в определенные моменты времени, и пользовательские функции, которые вы придумываете сами.

    Встроенная функция _ready()

    Функция _ready() — это точка входа. Она вызывается ровно один раз в тот самый момент, когда узел появляется в игре (например, когда игрок сажает фрукт на клетку).

    Команда print() выводит текст в консоль разработчика. Это отличный способ проверить, работает ли ваш код.

    Встроенная функция _process(delta)

    Функция _process(delta) — это сердцебиение игры. Она вызывается каждый кадр. Если ваша игра работает при 60 кадрах в секунду, эта функция выполнится 60 раз за одну секунду. Она идеально подходит для вещей, которые должны происходить постоянно, например, для перемещения врагов или летящих снарядов.

    Параметр delta — это время в секундах, прошедшее с предыдущего кадра (обычно это крошечное число, около 0.016). Использование delta критически важно для плавного движения.

    Формула перемещения выглядит так: .

    Если скорость зомби равна 50 пикселям в секунду, умножение на delta гарантирует, что он пройдет ровно 50 пикселей за секунду, независимо от того, тормозит ли компьютер игрока или выдает 144 кадра в секунду.

    Пользовательские функции

    Вы можете создавать свои собственные функции для специфических действий. Например, функция получения урона:

    Здесь amount — это аргумент функции. Когда враг кусает фрукт, он передает в эту функцию число (например, 10), и здоровье фрукта уменьшается на это значение.

    Условия: Принятие решений

    Игра не может быть линейной. Скрипт должен уметь принимать решения в зависимости от ситуации. Для этого используются условные операторы if (если) и else (иначе).

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

    В этом коде мы проверяем условие: . Если это правда, вызывается функция die().

    Команда queue_free() — это один из самых важных методов в Godot. Она безопасно удаляет узел (и все его дочерние элементы) из игры. Именно так умирают враги, исчезают снаряды при попадании и уничтожаются съеденные защитники.

    Практика: Создание снаряда (Instancing)

    В вашей игре Plants vs. Zombies + Blox Fruits защитники должны атаковать врагов. Для этого им нужно создавать магические снаряды и запускать их по линии.

    В предыдущей статье мы обсуждали концепцию экземпляров сцен (Instancing). Теперь мы реализуем это через код. Процесс создания нового объекта в игре состоит из трех шагов:

  • Загрузка чертежа. Сначала скрипт должен узнать, какую сцену мы хотим создать.
  • Создание экземпляра. Мы делаем копию этой сцены в оперативной памяти.
  • Добавление в мир. Мы помещаем созданную копию на игровое поле.
  • Предположим, вы уже создали отдельную сцену огненного шара и сохранили ее как fireball.tscn. Вот как выглядит скрипт вашего фрукта-защитника, который стреляет по сигналу таймера:

    Разберем самую сложную строчку: get_parent().add_child(new_fireball).

    Почему мы не можем просто написать add_child(new_fireball)? Если мы добавим снаряд как дочерний элемент самого фрукта, то снаряд станет частью фрукта. Если фрукт съедят (он вызовет queue_free()), летящий снаряд исчезнет вместе с ним!

    Поэтому мы используем get_parent(), чтобы обратиться к главному игровому уровню (родителю фрукта), и просим уровень стать родителем для нового огненного шара. Теперь снаряд летит сам по себе и не зависит от жизни стрелка.

    Взаимодействие между объектами

    В гибриде Blox Fruits и PvZ объекты постоянно взаимодействуют: враги наступают на ловушки, снаряды попадают в зомби. Как скрипт снаряда понимает, что он во что-то попал?

    Для этого используются сигналы (Signals). Узлы типа Area2D (которые мы используем для снарядов и врагов) имеют встроенный сигнал area_entered. Он автоматически срабатывает, когда две физические зоны пересекаются.

    Пример скрипта для огненного шара:

    В этом коде мы используем систему групп (Groups). В Godot вы можете добавить любого врага в группу enemies. Когда снаряд во что-то врезается, он сначала проверяет: «А враг ли это?». Если да, он передает ему свой урон и исчезает.

    Освоив переменные, функции, условия и создание экземпляров, вы получили в свои руки все необходимые инструменты для реализации базового геймплея. Вы уже можете посадить фрукт, заставить его стрелять по таймеру, двигать снаряды и наносить урон врагам. В следующих шагах мы углубимся в создание сетки (Grid) для правильного размещения защитников по линиям.