#2 Godot: 3D Platformer от BornCG. Объекты и физическое взаимодействие объектов
Сегодня мы рассмотрим:
- создание 3D-уровня с взаимодействующими объектами;
- создание падающих блоков как многократно используемых объектов;
- использование физического движка Godot.
Открываем наш проект с помощью кнопки Редактировать в менеджере проектов (показываю последний раз, в дальнейшем эту операцию опустим как повторяющуюся).
Мы видим нашу сцену на том месте, на котором мы остановились: добавлен корневой узел сцены Level1, добавлен узел камеры Camera3D.
Теперь мы добавим в нашу сцену пол (floor), по сути являющийся физическим объектом, с которым взаимодействуют все другие объекты. В панели Сцена выделяем корневой узел Level1 и нажимаем +. В поиск вводим StaticBody3D и добавляем выбранный узел. Static в данном случае означает, что данный физический объект не будет двигаться в нашей сцене, в отличие от анимированных объектов. Сразу переименуем новый узел и гордо назовём его Floor.
Жёлтый треугольник рядом с узлом Floor - это следующее предупреждение:
Значит, нужно добавить узлу StaticBody3D (наш Floor) форму (shape). Для этого выделим Floor и добавим уже дочерний к нему, а не к корневому узлу сцены, как раньше, узел MeshInstance3D.
Теперь, пока новый узел MeshInstance3D выделен, добавим ему некоторые свойства в Инспекторе: мы видим, что строка Mesh пуста, то есть у нашего узла нет никакой структуры (вершин, рёбер, полигонов).
Открываем выпадающий список в этой строчке и выбираем Создать BoxMesh.
Кроме изменений в Инспекторе, замечаем, что в рабочем пространстве у нас так же появился кубик.
Однако мы же хотели сделать пол, то есть наш кубик должен быть более вытянутым в длину и ширину, и более сжатым в высоту. Чтобы внести такие форменные изменения, в Инспекторе в строке Mesh кликаем на изображение нашего кубика, чтобы там же раскрыть его свойства.
Видим строку Size, и поскольку у нас X и Z - это ширина и длина объекта, а Y - его высота, сделаем наш кубик размерами 10х10х0.5.
Отлично, но помогло ли это нам избавиться от жёлтого треугольника рядом с узлом Floor в панели Сцена?
В описании проблемы указано, что нам нужно добавить дочерний узел CollisionShape3D, чтобы задать форму узлу StaticBody3D (наш Floor).
Мы не сделали этого раньше, потому что у нас не было меша для узла StaticBody3D, и узел CollisionShape3D в таком случае оставался бы без формы. А это же пол, мы же не хотим провалиться...
Исправляем. Выделяем наш Floor, и добавляем для него дочерний узел CollisionShape3D.
Кликаем на предупреждение, читаем, изучаем.
Как удачно, что мы буквально вот только создали объект формы MeshInstance3D. Для узла CollisionShape3D в Инспекторе есть специальная строка свойств - Shape.
Открываем выпадающий список и выбираем Создать BoxShape3D.
И замечаем, что в рабочем пространстве у нас появился кубик, но странный.
Всё идёт к тому, что этому кубику нам нужно придать такие же размеры, как и узлу MeshInstance3D. Для этого в Инспекторе в строке Shape кликаем на выбранный нами ранее BoxShape, и в раскрытом окне свойств пишем для CollisionShape3D нужные значения (напомню, 10х10 и 0.5 в высоту).
Кажется, мы достигли успеха без предупреждений: теперь у узла StaticBody3D (наш Floor) есть физическая форма и форма, которая будет учитываться движком при обработке столкновений (речь об MeshInstance3D и CollisionShape3D соответственно, если вы не поняли).
Интересный момент: если в панели Сцена мы выделим узел Floor, который является корневым узлом для MeshInstance3D и CollisionShape3D, и будем с ним каким-либо образом взаимодействовать в рабочем пространстве (перемещать, вращать, изменять форму и т.д.), то изменяться будут и оба дочерних узла. Однако если работать только с одним дочерним узлом, то и изменяться будет только он. В целом, логично. Однако такое поведение может создать ситуацию, когда вроде бы единый объект Floor будет на самом деле "разбросан" по рабочему пространству из-за того, что мы, например, переместили CollisionShape3D, и теперь обработка столкновений происходит не на объекте, а где-то рядом.
Чтобы облегчить самим себе жизнь, сгруппируем все дочерние узлы нашего Floor-а. Для этого в панели Сцена выделим узел Floor, и в рабочем пространстве, в его верхней панели инструментов, нажмём на кнопку Сгруппировать выделенные узлы (Ctrl+G).
После группировки дочерние узлы станут недоступны для выбора, и если мы в рабочем пространстве тыкнем на наш объект, то выберем сразу узел Floor.
Ну и кстати, продемонстрируем позитивные изменения в жизни узла Floor. Нажмём на кружок Z в правом верхнем углу рабочего пространства (там расположена специальная штука для ориентации в трёхмерном пространстве, будем называть её, например... гизмо, от слова gizmo), чтобы взглянуть на наш объект сбоку.
Очевидно, наш пол сейчас расположен выше нулевой отметки (оранжевая линия поперёк рабочего пространства, она же ось X). Берёмся за вертикальную зелёную стрелочку перемещения объекта по оси Y (то есть вертикальной оси) и перемещаем объект чуть ниже нулевой отметки.
Объект не рассыпался на составляющие, что не может не радовать.
Однако пора посмотреть на результат наших стараний со стороны: сохраняемся (Ctrl+S) и запускаем нашу сцену (F6).
Мы видим наш пол, что уже неплохо. Но тут у читателя может возникнуть закономерный вопрос:
Самое обидное, что всё серое только при запуске сцены, а в рабочем пространстве-то всё нормально. К слову, за то, чтобы в рабочем пространстве всё было красиво, отвечают эти две кнопки в панели инструментов.
Если мы их отключим, то...
Теперь в рабочем окружении мы видим всё таким, как оно есть. Очевидно, нам не хватает огонька в нашей работе. В панели Сцена выделяем наш самый родительский узел - Level1 - и добавляем ему дочерний узел DirectionalLight3D.
Посмотрим на наше солнышко в рабочем пространстве.
Приподнимем его по оси Y и немного повращаем.
Раньше мы и не замечали, но у солнца есть направление потока лучей, что в перспективе даст нам красивую тень на наших объектах. Поэтому выставим солнце немного криво, для эффекта.
Отлично, сохраняем изменения и запускаем сцену.
Итак, подведя промежуточные итоги - мы сделали наш первый трёхмерный физический объект (Floor), используя три различных узла для этого: один корневой и два дочерних. Наша следующая задача - добавить на сцену динамики. Пусть это будут... падающие блоки, которые мы сделаем с помощью реюза одного падающего блока, который мы сделаем прямо сейчас.
Нажимаем на + возле вкладки нашего рабочего пространства и добавляем новую сцену.
Дефолтные корневые узлы в панели Сцена нам не подходят, поскольку мы хотим создать не полноценную сцену, а один лишь объект. Поэтому нажимаем на + в панели Сцена и добавляем узел RigidBody3D, который может использоваться для физической симуляции (так у него в описании написано).
Дальше всё по накатанной: добавляем для корневого узла RigidBody3D дочерний MeshInstance3D и с помощью Инспектора задаём ему меш в виде куба (BoxMesh).
Следом добавляем её один дочерний узел CollisionShape3D и с помощью Инспектора задаём ему форму в виде куба (BoxShape3D).
Поскольку мы не меняли стандартные размеры кубика при создании узла MeshInstance3D, то размеры кубиков обоих дочерних узлов совпали. Удобно!
Переименуем корневой узел RigidBody3D как Block и сохраним нашу дополнительную сцену.
Теперь осталось добавить Block на сцену Level1. Для этого в панели Сцена выделяем корневой узел Level1 и нажимаем на кнопку Инстанцировать дочернюю сцену (Ctrl+Shift+A), которая находится справа от привычной нам кнопки +.
В открывшемся окошке видим кнопку Нечёткий Поиск
...не обращаем на неё внимания и совершаем очевидный выбор.
Тут в обучающем видео нас, кстати, предупреждают от инстанцирования в качестве дочерней нашей текущей сцены (это если бы выбрали не block.tscn, а level_1.tscn) - ну, не очень-то и хотелось.
Обращаем внимание, что в рабочем пространстве выпал кубик.
Поскольку заявлено, что это падающий сверху вниз блок, немного поднимаем его по оси Y.
Кстати, вы заметили, что мы не группировали дочерние узлы, когда делали Block, а тем не менее в нашей текущей сцене он ведёт себя как единое целое? Магия, да и только.
Кстати (х2), вот эта иконка возле нашего Block позволяет, кликнув по ней, перейти на вкладку block, даже если мы её закроем. Удобно.
Кстати (х3), изменения во вкладке block, которые мы можем совершить над нашим Block, сразу же отобразятся во всех сценах, где мы этот Block используем. Крайне удобно.
Итак, запустим нашу сцену.
Выглядит неплохо, наш Block действительно падает. Однако кое-чего не хватает, и в данном случае не хватает теней. Добавим: выделим в панели Сцена узел DirectionalLight3D, в Инспекторе откроем свойство Shadow и включим эту опцию.
Больше блоков богу блоков: выделяем в панели Сцена узел Block, изменяем его имя на Block1, кликаем на него правой кнопкой мыши и выбираем опцию Дублировать (Ctrl+D).
Да, всё верно, циферку 1 в имени Block мы поставили, чтобы движок при дублировании объекта автоматом поставил дубликату следующий номер.
Передвинем Block2 и немного покрутим.
Ещё, ещё! Зажимаем Shift, выделаем в панели Сцена сразу Block1 и Block2, дублируем, получаем ещё два блока.
Перемещаем их, крутим-вертим.
А сейчас финальный твист: переходим на вкладку block и делаем наш Block более Block-ным. По очереди меняем свойство Size у узла CollisionShape3D и у узла MeshInstance3D, делаем размер кубика по оси Y равным 0.5.
В рабочем пространстве вкладки block видны изменения.
Переходим на вкладку level_1, иии...
Ради этого всё и затевалось, да. Приятное ощущения впитывания лучшей практики работы с одинаковыми объектами.
Подводя личные итоги - мой план работает (пока), обязательства перед самим собой и перед читателями (если таковые имеются) заставили меня сжать булки и дописать эту статью, которую я начал ещё в понедельник. Плюс, кажется, я так лучше запоминаю, что делаю.
Ждём продолжения.