И снова привет, Хабр! В прошлой статье я рассказал, как создавать 2D-игры на движке Godot. По вашим запросам — добавляем измерение и переходим в мир 3D. На этот раз мы погрузимся в трехмерные объекты и элементы анимирования. Подробности под катом!

Введение


Любая игра состоит из двух основных аспектов: дизайна и функциональной части. Создать дизайн для 3D-игр мне нравится больше, ведь он более «полноценный».

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


Разработка дизайна


Если вы не читали первую часть и не знаете, как создать проект, — самое время сделать это, чтобы создать проект. Далее нужно указать корневой узел — 3D-сцену. По сути, это наш родительский класс, внутри которого будут все объекты будущей игры.

Создание мира


Создадим дочерний узел WorldEnvironment и перенесем в него объект World. По сути, это глобальное окружение, в котором мы можем указывать различные настройки отображения сцены. Например, какое освещение у нас будет на сцене.


Выбираем сам объект и в Инспекторе указываем, что создаем новое окружение и разрешаем его редактирование.


Здесь заходим в background и видим, что в качестве фона установлен обычный цвет. Давайте это поменяем и укажем, что background будет неким материалом Sky — ProceduralSkyMaterial.


Дополнительно зайдем в town map и укажем, какой свет будет применяться к сцене. На данный момент указан Linear, его можно изменить, например, на Filmic.


Кроме того, если хотите, можете увеличить яркость. Для этого просто увеличиваете Exposure. Я оставлю значение по умолчанию. При необходимости можете попробовать Auto Exposure.


Конечно, есть масса и других настроек. Например, мы можем изменить цвет окружения. Для этого переходим в ProceduralSkyMaterial / Sky и настраиваем отображение под свой проект. То же самое делаем с параметром Ground:


Следующим этапом попробуем добавить мир-ассет, установленный с сайта. Для заходим папку World, дальше — в source. Перетаскиваем объект в это пространство.


Расположим координаты мира по нулям, чтобы он находился в точке отсчета. Также скорректируем его размеры, чтобы все текстуры отображались корректно. Кроме того, поднимем его повыше, чтобы мир находился над условной сеткой. Должно получиться вот так:


Визуальное сопровождение


Следующим этапом добавим камеру. Нажимаем на родительский объект, кликаем на Добавить дочерний узел и выбираем обычную камеру.


Добавим узел освещения, чтобы отображались тени и был свет.


Возможные ошибки переноса


Есть нюанс. Возможно, вы скачали другой ассет и самостоятельно попробовали его расположить на сцене. Но при этом он импортировался как «серый объект» без каких-либо цветов.

Это может быть связано с тем, что сам 3D-объект был некорректно экспортирован из первоначальной программы, в которой разрабатывался. Соответственно, в таком случае нужно всю папку с этим объектом забросить в Godot. Тогда, скорее всего, все текстуры загрузятся.


Остается сделать Ctrl + S — и готово.

Проектировка своих объектов


Теперь давайте попробуем создать свои собственные объекты внутри игрового движка. Для начала необходимо добавить новый дочерний узел — CSGCombiner.


Внутрь этого объекта мы можем поместить другие 3D-объекты. Все они идут с названием CSG, box, cylinder, mesh, polygon. Мы можем легко менять их размеры, например ширину или высоту, а также углы. Или вообще трансформировать в нечто абсолютно не похожее на первоначальную форму.

Простые объекты


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


Объект csgBox — это «нечто серое и пустое». Простая заготовка, на которую можно натянуть текстуру и добавить «твердотельность». Для этого выбираем сам объект, выбираем Material и нажимаем на <пусто> и указываем StandardlMaterial3D.


Следующее, что необходимо сделать, — выбрать этот материал. Переходим в Albedo. Здесь можем указать текстуру.


Мы можем настраивать визуальную часть текстуры объекта: изменять ее цвет, яркость и другие свойства. Таким образом, мы с вами создаем объект и можем легко изменять его характеристики. Но это не единственная особенность csgBox-объектов.

Усложненные объекты


Дополнительная особенность: мы объединять объекты и вырезать из них выделенные области. Давайте попробуем!


Чтобы объединить объекты, достаточно оставить параметр в CSGShape3D, Operation – Union. А чтобы вырезать один объект из другого, Operation – Subtraction. Для создания объектов из нескольких, нужно создавать дочерние узлы от CSGCombiner. Выглядит это примерно так:


Также для примера рассмотрим еще такой объект:


Тут я использую узел CSGPolygon, он позволяет рассмотреть такую операцию как Mode – Spin, то есть мы задаем форму и прокручиваем по оси и получаем тело вращения. Также тут видно что мы можем менять цвет текстуры.

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

Поэтому мы давайте сделаем их твёрдыми объектами, к нашему счастью это делается максимально просто, мы выбираем CSGCombiner и здесь нам все что необходимо сделать так это установить галочку Use Collision


Создание каркаса


Давайте добавим объект, который будет твердой поверхностью для всей нашей сцены. Сейчас все красиво, но не осязаемо.

В WorldEnvironment добавляем новый дочерний узел StaticBody. По сути, это некий родительский элемент, внутри которого есть разные статичные объекты.


Добавим новый дочерний узел — CollisionShape и укажем CylinderShape. И все, что необходимо сделать, — это увеличить размеры самого объекта. Так нужно сделать для каждого предмета в сцене с которым вы будете взаимодействовать. Например, деревьями, колодцами и домами.


Создание основного игрока


Итак, мы можем приступить к разработке основного игрока. В качестве него можно использовать любой объект, например, скачанный с сайта SketchFab.

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


Далее разархивируйте папку с объектом и перетащите внутрь Godot. Лучше всего создать отдельную сцену с персонажем — так будет удобнее редактировать его.

Создаем узел CharacterBody3D, добавляем к нему персонажа и создаем узел CollisionShape, чтобы не было ошибок. Должно получиться вот так:


Теперь выбираем форму твердого объекта. Оптимальнее всего — CoupseShape, так как с ним проще работать


Скрипты


Прикрепляем скрипт к основному игроку и поэтому нажимаем на CharacterBody. Далее — на Новый скрипт.


Начнем с обработки переменных, которая «вшита» в декоратор @export. Они контролируют скорость движения, прыжка, свободного падения и чувствительность мыши.


Для обработки внутренних переменных использую:


movementInputVector используется для вектора движения персонажа по горизонтали (X и Z), а currentyYVelocity — для текущей вертикальной скорости (ось Y).

Мы добавили узел камеры. Чтобы он не был статичным и следовал за персонажем, нужно написать обработку движения мыши:


Функция _input обрабатывает события ввода. В данном случае, если событие — движение мыши (InputEventMouseMotion), персонаж поворачивается вокруг вертикальной оси (ось Y) на угол, пропорциональный движению мыши и чувствительности.

Обработка физики и движения выглядит так:


Функция _physics_process вызывается каждый кадр и обрабатывает физику персонажа.

1. movementInputVector получает вектор движения из ввода пользователя (WASD или стрелки) и умножает его на скорость движения.

2. Если персонаж находится на земле (is_on_floor), вертикальная скорость (currentyYVelocity) сбрасывается в 0. Если пользователь нажимает кнопку прыжка (jump), вертикальная скорость устанавливается в значение jumpVelocity.

3. Если персонаж не на земле, к вертикальной скорости применяется гравитация, уменьшая ее на значение gravitySpeed. Но не более чем 2 000 единиц, что предотвращает бесконечное ускорение вниз.

4. Вычисляется итоговая скорость velocity, которая включает горизонтальное движение и вертикальную скорость. Горизонтальное движение преобразуется в соответствии с ориентацией персонажа (transform.basis).

5. move_and_slide автоматически обрабатывает столкновения объекта с другими объектами, которые имеют Collision.

Анимация персонажа


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

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


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


Теперь нам нужно добавить новый дочерний узел к персонажу — AnimationTree. С помощью него вы, по сути, определяете разные положения или анимации, которые есть у объекта. Далее в инспекторе выбираем тип анимации. Лично мне удобнее применить StateMachine, но вы можете использовать свой тип.


Справа мы можем перенести узел AnimationPlayer в параметр Anim Player, чтобы синхронизировать наши узлы анимаций. Внизу должно появиться пространство для создания блоков анимаций и настройки связности между ними.


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

Используем соединитель узлов и проводим связи:


Обычно при подключении двух блоков изображение начинает сходить с ума и работать некорректно. Дело в том, что наши задачи запускаются одновременно, но это просто исправить. Нажимаем на связь и в параметрах справа выбираем Advance–Condition. Прописываем условие — пусть это будет, например, ходьба.


То же самое повторяем для другой связи, но в Condition указываем Idle. Если мы вернемся обратно в AnimationTree, то увидим, что в параметрах появились две новые функции, которые будем вызывать:


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


Тестирование


Нам остается лишь протестировать проект — нажимаем на иконку запуска. Если вы хотите экспортировать проект под запуск на определенной платформе, это можно сделать во вкладке проект–экспортировать.


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


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

Что думаете насчет движка Godot вы? Поделитесь своим мнением в комментариях!