![](https://habrastorage.org/webt/p0/db/gf/p0dbgfb70vpd63kwokvj4dpnoq0.png)
И снова привет, Хабр! В прошлой статье я рассказал, как создавать 2D-игры на движке Godot. По вашим запросам — добавляем измерение и переходим в мир 3D. На этот раз мы погрузимся в трехмерные объекты и элементы анимирования. Подробности под катом!
Введение
Любая игра состоит из двух основных аспектов: дизайна и функциональной части. Создать дизайн для 3D-игр мне нравится больше, ведь он более «полноценный».
Поскольку я не дизайнером и не профессиональный разработчик, не буду с нуля создавать все объекты. Просто создам внешний мир, расставлю деревья, подготовлю окружение и саму поверхность. Для этого всего буду использовать готовый ассет. Но под конец статьи все же попробую самостоятельно создать 3D-объекты. Так что не переключайтесь.
![](https://habrastorage.org/webt/qo/sm/2w/qosm2wbok56o-xqniese_2auboo.png)
Разработка дизайна
Если вы не читали первую часть и не знаете, как создать проект, — самое время сделать это, чтобы создать проект. Далее нужно указать корневой узел — 3D-сцену. По сути, это наш родительский класс, внутри которого будут все объекты будущей игры.
Создание мира
Создадим дочерний узел WorldEnvironment и перенесем в него объект World. По сути, это глобальное окружение, в котором мы можем указывать различные настройки отображения сцены. Например, какое освещение у нас будет на сцене.
![](https://habrastorage.org/webt/uc/7m/tm/uc7mtmzd8h00ljduhar7ocknfhw.png)
Выбираем сам объект и в Инспекторе указываем, что создаем новое окружение и разрешаем его редактирование.
![](https://habrastorage.org/webt/tg/9i/sy/tg9isy7srzqak4eq31pjqi6bvte.png)
Здесь заходим в background и видим, что в качестве фона установлен обычный цвет. Давайте это поменяем и укажем, что background будет неким материалом Sky — ProceduralSkyMaterial.
![](https://habrastorage.org/webt/og/8z/yg/og8zygl7wn9t5wcczc1yu9iuivq.png)
Дополнительно зайдем в town map и укажем, какой свет будет применяться к сцене. На данный момент указан Linear, его можно изменить, например, на Filmic.
![](https://habrastorage.org/webt/_y/jz/sj/_yjzsjsdsieceilkvpn2fdjqgzg.png)
Кроме того, если хотите, можете увеличить яркость. Для этого просто увеличиваете Exposure. Я оставлю значение по умолчанию. При необходимости можете попробовать Auto Exposure.
![](https://habrastorage.org/webt/w2/nt/dp/w2ntdpqof-ygm7_rzllcohhytcy.png)
Конечно, есть масса и других настроек. Например, мы можем изменить цвет окружения. Для этого переходим в ProceduralSkyMaterial / Sky и настраиваем отображение под свой проект. То же самое делаем с параметром Ground:
![](https://habrastorage.org/webt/47/ao/xw/47aoxwvd7wq9zzedsi4y0_kf6w4.png)
Следующим этапом попробуем добавить мир-ассет, установленный с сайта. Для заходим папку World, дальше — в source. Перетаскиваем объект в это пространство.
![](https://habrastorage.org/webt/3f/jw/v3/3fjwv3jqhnzixgnkv5f_ipuaogg.png)
Расположим координаты мира по нулям, чтобы он находился в точке отсчета. Также скорректируем его размеры, чтобы все текстуры отображались корректно. Кроме того, поднимем его повыше, чтобы мир находился над условной сеткой. Должно получиться вот так:
![](https://habrastorage.org/webt/9p/-m/gv/9p-mgv7mg275sqsyuw3ea2qvv6o.png)
Визуальное сопровождение
Следующим этапом добавим камеру. Нажимаем на родительский объект, кликаем на Добавить дочерний узел и выбираем обычную камеру.
![](https://habrastorage.org/webt/4a/7k/gf/4a7kgfme1vfuuvmexbx881gzmko.png)
Добавим узел освещения, чтобы отображались тени и был свет.
![](https://habrastorage.org/webt/dd/9p/ei/dd9peiofsmytgbzslgxlmlkf8b0.png)
Возможные ошибки переноса
Есть нюанс. Возможно, вы скачали другой ассет и самостоятельно попробовали его расположить на сцене. Но при этом он импортировался как «серый объект» без каких-либо цветов.
Это может быть связано с тем, что сам 3D-объект был некорректно экспортирован из первоначальной программы, в которой разрабатывался. Соответственно, в таком случае нужно всю папку с этим объектом забросить в Godot. Тогда, скорее всего, все текстуры загрузятся.
![](https://habrastorage.org/webt/iq/y2/3i/iqy23imorlhjii0cfnngvdmhiag.png)
Остается сделать Ctrl + S — и готово.
Проектировка своих объектов
Теперь давайте попробуем создать свои собственные объекты внутри игрового движка. Для начала необходимо добавить новый дочерний узел — CSGCombiner.
![](https://habrastorage.org/webt/tz/w6/0k/tzw60kpqfoliwzfy5v9vixe4utw.png)
Внутрь этого объекта мы можем поместить другие 3D-объекты. Все они идут с названием CSG, box, cylinder, mesh, polygon. Мы можем легко менять их размеры, например ширину или высоту, а также углы. Или вообще трансформировать в нечто абсолютно не похожее на первоначальную форму.
Простые объекты
Для начала добавим csgBox и посмотрим, как с ним можно взаимодействовать.
![](https://habrastorage.org/webt/6j/nx/0p/6jnx0paoiqr2nesgyxa0ktv8szm.png)
Объект csgBox — это «нечто серое и пустое». Простая заготовка, на которую можно натянуть текстуру и добавить «твердотельность». Для этого выбираем сам объект, выбираем Material и нажимаем на <пусто> и указываем StandardlMaterial3D.
![](https://habrastorage.org/webt/pk/19/_-/pk19_-9iwpxzgeltgbpiuf-hzkw.png)
Следующее, что необходимо сделать, — выбрать этот материал. Переходим в Albedo. Здесь можем указать текстуру.
![](https://habrastorage.org/webt/mq/ks/dh/mqksdh6mvs5umhiii7lkhehdmjg.png)
Мы можем настраивать визуальную часть текстуры объекта: изменять ее цвет, яркость и другие свойства. Таким образом, мы с вами создаем объект и можем легко изменять его характеристики. Но это не единственная особенность csgBox-объектов.
Усложненные объекты
Дополнительная особенность: мы объединять объекты и вырезать из них выделенные области. Давайте попробуем!
![](https://habrastorage.org/webt/iu/7o/ki/iu7okif4qetjft7x8cuhjkojcrc.png)
Чтобы объединить объекты, достаточно оставить параметр в CSGShape3D, Operation – Union. А чтобы вырезать один объект из другого, Operation – Subtraction. Для создания объектов из нескольких, нужно создавать дочерние узлы от CSGCombiner. Выглядит это примерно так:
![](https://habrastorage.org/webt/s3/8h/m6/s38hm6rk8rz9ny9xn-yffkubxfk.png)
Также для примера рассмотрим еще такой объект:
![](https://habrastorage.org/webt/yj/xd/9r/yjxd9r4k_cvhtu-fqdvjj_ntixe.png)
Тут я использую узел CSGPolygon, он позволяет рассмотреть такую операцию как Mode – Spin, то есть мы задаем форму и прокручиваем по оси и получаем тело вращения. Также тут видно что мы можем менять цвет текстуры.
Поскольку здесь у нас будет находиться игрок, а игрок это у нас будет твердый объект у которого к тому же будет еще и физика, физические различные свойства, то сам этот твёрдый объект, также должен находиться на неких твердых объектах иначе он просто будет проваливаться и лететь вниз до бесконечности.
Поэтому мы давайте сделаем их твёрдыми объектами, к нашему счастью это делается максимально просто, мы выбираем CSGCombiner и здесь нам все что необходимо сделать так это установить галочку Use Collision
![](https://habrastorage.org/webt/kn/h4/pc/knh4pc77ld46jk7xvrn05fpd0ao.png)
Создание каркаса
Давайте добавим объект, который будет твердой поверхностью для всей нашей сцены. Сейчас все красиво, но не осязаемо.
В WorldEnvironment добавляем новый дочерний узел StaticBody. По сути, это некий родительский элемент, внутри которого есть разные статичные объекты.
![](https://habrastorage.org/webt/a0/xo/7p/a0xo7pis62wo1gpcmebd7zmad9g.png)
Добавим новый дочерний узел — CollisionShape и укажем CylinderShape. И все, что необходимо сделать, — это увеличить размеры самого объекта. Так нужно сделать для каждого предмета в сцене с которым вы будете взаимодействовать. Например, деревьями, колодцами и домами.
![](https://habrastorage.org/webt/ed/kn/km/edknkmk5tnvtf9vfedfetmvlwxg.png)
Создание основного игрока
Итак, мы можем приступить к разработке основного игрока. В качестве него можно использовать любой объект, например, скачанный с сайта SketchFab.
Я выбрал такую птичку, на которую можно в дальнейшем наложить свои анимации:
![](https://habrastorage.org/webt/a_/mi/o7/a_mio7wchpqqtnbmv5khdiyrbei.png)
Далее разархивируйте папку с объектом и перетащите внутрь Godot. Лучше всего создать отдельную сцену с персонажем — так будет удобнее редактировать его.
Создаем узел CharacterBody3D, добавляем к нему персонажа и создаем узел CollisionShape, чтобы не было ошибок. Должно получиться вот так:
![](https://habrastorage.org/webt/qw/zf/du/qwzfduncon-thdb27ivfjwuygzy.png)
Теперь выбираем форму твердого объекта. Оптимальнее всего — CoupseShape, так как с ним проще работать
![](https://habrastorage.org/webt/eh/ar/3k/ehar3kgs9kdswgsbkucdknqfdwo.png)
Скрипты
Прикрепляем скрипт к основному игроку и поэтому нажимаем на CharacterBody. Далее — на Новый скрипт.
![](https://habrastorage.org/webt/bu/wr/lb/buwrlbfsxabnnbawn5cqd6jdbjy.png)
Начнем с обработки переменных, которая «вшита» в декоратор @export. Они контролируют скорость движения, прыжка, свободного падения и чувствительность мыши.
![](https://habrastorage.org/webt/us/w_/o5/usw_o5hjy4gw3d8nb1jyjysewzy.png)
Для обработки внутренних переменных использую:
![](https://habrastorage.org/webt/nf/uh/de/nfuhdes6ujllaihf41box4pwj9a.png)
movementInputVector используется для вектора движения персонажа по горизонтали (X и Z), а currentyYVelocity — для текущей вертикальной скорости (ось Y).
Мы добавили узел камеры. Чтобы он не был статичным и следовал за персонажем, нужно написать обработку движения мыши:
![](https://habrastorage.org/webt/hg/-z/5r/hg-z5rl_dhbgbklsx7gnbb7nadc.png)
Функция _input обрабатывает события ввода. В данном случае, если событие — движение мыши (InputEventMouseMotion), персонаж поворачивается вокруг вертикальной оси (ось Y) на угол, пропорциональный движению мыши и чувствительности.
Обработка физики и движения выглядит так:
![](https://habrastorage.org/webt/on/bq/q9/onbqq93insoeslo5h6hqkihbxae.png)
Функция _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. Здесь мы можем проиграть саму анимацию. То есть анимация по умолчанию есть и мы можем ее запускать.
![](https://habrastorage.org/webt/lg/hm/iy/lghmiy6xc2dd4cvl8-i3dy0tyjo.png)
В этом узле можно создавать свои анимации или управлять текущими. Например, я создал статичное изображение из подвижной анимации игрока:
![](https://habrastorage.org/webt/lv/gl/5-/lvgl5-wid0uvqghafmuf7q9uwdw.png)
Теперь нам нужно добавить новый дочерний узел к персонажу — AnimationTree. С помощью него вы, по сути, определяете разные положения или анимации, которые есть у объекта. Далее в инспекторе выбираем тип анимации. Лично мне удобнее применить StateMachine, но вы можете использовать свой тип.
![](https://habrastorage.org/webt/st/2n/m-/st2nm-go1z5ye5xresvys5llbwg.png)
Справа мы можем перенести узел AnimationPlayer в параметр Anim Player, чтобы синхронизировать наши узлы анимаций. Внизу должно появиться пространство для создания блоков анимаций и настройки связности между ними.
![](https://habrastorage.org/webt/gj/p4/xs/gjp4xsmou3dkxviuhsmrlyn6gge.png)
После мы можем нажать на каждый из блоков и система будет проигрывать анимацию. На этом этапе необходимо настроить связь между элементами, чтобы они вызывались отдельно и при определенной команде.
Используем соединитель узлов и проводим связи:
![](https://habrastorage.org/webt/3u/q0/ev/3uq0evvsctcsdq10obf2dhykx3i.png)
Обычно при подключении двух блоков изображение начинает сходить с ума и работать некорректно. Дело в том, что наши задачи запускаются одновременно, но это просто исправить. Нажимаем на связь и в параметрах справа выбираем Advance–Condition. Прописываем условие — пусть это будет, например, ходьба.
![](https://habrastorage.org/webt/ec/hl/n4/echln4z3ueyhkxwh7m8v-rtrh6a.png)
То же самое повторяем для другой связи, но в Condition указываем Idle. Если мы вернемся обратно в AnimationTree, то увидим, что в параметрах появились две новые функции, которые будем вызывать:
![](https://habrastorage.org/webt/c7/m-/uq/c7m-uqjoebyrjoycfa3dbxoldi8.png)
Остался вопрос в том, как нам контролировать это через код. На самом деле, это действительно просто. Если мы перейдем к скрипту персонажа, мы можем настроить код для работы с переходом между состояниями:
![](https://habrastorage.org/webt/yw/2c/fp/yw2cfpbmraxfsq1_vqohn-mknq0.png)
Тестирование
Нам остается лишь протестировать проект — нажимаем на иконку запуска. Если вы хотите экспортировать проект под запуск на определенной платформе, это можно сделать во вкладке проект–экспортировать.
![](https://habrastorage.org/webt/hm/gv/ec/hmgvecf--u4nxtx7d4soyehu1u4.png)
Прежде чем мы выполним построение проекта под платформу, ее нужно будет скачать внутрь Godot.
![](https://habrastorage.org/webt/jg/_r/wb/jg_rwbblitsztnmbly6peea5xr4.gif)
Запускаем проект и видим, что весь дизайн и функции обрабатываются корректно. Сама игра — лишь демонстрация, которая может стать хорошей базой для более проработанных проектов.
Что думаете насчет движка Godot вы? Поделитесь своим мнением в комментариях!
Комментарии (6)
jonic
02.05.2024 16:23+2А я за cocos creator взялся, исходники открыты, с документацией мутновато.. но в общем все открыто и читабельно. Интересная штука в общем.
cjmaxik
02.05.2024 16:23+2Функция _physics_process вызывается каждый кадр
Неверно, данная функция независима от количества кадров в секунду. _process как раз таки вызывается каждый кадр.
Octabun
Извольте, сударь! Про Godot я думаю что внешне он прекрасный, полностью кросс-платформенный, скриптуется на нормальном языке С++ и на относительно нормальном C# (GDScript - очередной язык с областью примениеости из одного кейса - спасибо, но нет, кроме того, это uncanny valley вокруг Python), работает на слабых машинах - да о таком можно только мечтать!
А если присмотреться - то Godot мутный. Скрипты на С++ тянут за собой свою сборочную систему и полный исходный код движка. Любям такое показывать не стыдно? Хоть бы под капот спрятали... Хотя это на первой странице С++ скрирты - это ух, а далее - это experimental, может и рано им ещё под капот. C# в Godot 3 в браузере работал, в Godot 4 больше не работает... Ломать не строить, да? Пиарить Godot стали вдруг, сразу, и отвсюду... как-то не с проста это всё.
Вот это самое интересное поскольку именно такое и обламывает обычно в самом конце, особенно подозрительным становишся узнав что всё везде и вместе со всем точно не работает, даже официально.
Я собираюсь присмотреться к Godot. Давно уже собираюсь и, вероятно, ещё очень долго буду собираться. Чтобы собрался быстрее - должен читать про то что реально работает, как Godot стоит среди одноклассников, сколько весит и как быстро запускается, а не вот это всё - деревья расставлять.
И ещё ИМХО - 3D из статьи выглядит просто убого.
OrkBiotechnologist
Последняя волна началась после того как руководителям Unity в голову ударила богатая мочевиной субстанция, решив что изменить лицензию на формат - «плати фиксированные отчисления, за каждое скачивание приложения» - это хорошая идея.
Octabun
Мне кажется что картина шире. Когда Эппл представляла М1, Unreal никак не упоминался, а Unity присутствовала и обещала скорейшую нативную поддержку процессора. Заодно была поставлена задача превращения Эппл в ведущую игровую платформу, ну хотя бы одну из.
После этого субстанция внезапно ударила в голову и Unreal и Unity. Godot тут выглядит разменной монетой в игре высших сил.
OrkBiotechnologist
Странно было бы, если бы он упоминался, так как у Apple с Epic Games уже который год идут судебные баталии. В определённый момент была ненулевая вероятность, что из Appstore пнут не только Fortnite, но и вообще все проекты на Unreal Engine.