#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

А мы двигаемся дальше.

Сегодня рассмотрим:

  • регистрацию столкновений;
  • удаление объектов, с которыми сталкивается игровой персонаж.

Вот как выглядит наш проект на текущий момент:

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

На предыдущем уроке мы добавили на нашу сцену Level1 спрайт Icon и анимировали его путём добавления функции вращения.

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

  • они являются переиспользуемыми объектами, то есть нам нужно будет создать одну монетку, экземпляры которой мы затем разместим на уровне;
  • монетки вращаются (потому что могут);
  • монетки исчезают после их касания игровым персонажем.

Что ж, приступим. Создаём новую вкладку в рабочем пространстве и добавляем корневой узел Area3D, который является областью, обнаруживающей входящие в неё объекты.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

Предсказуемо переименуем корневой узел Area3D в Coin.

Помните Area3D? Это он сейчас
Помните Area3D? Это он сейчас

Мы видим жёлтый треугольник предупреждения возле корневого узла Coin - так как узел Area3D является областью в пространстве, ему нужна форма. Ну и меш, естественно, тоже.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

Добавляем дочерний узел MeshInstance3D, в Инспекторе в свойстве Mesh выбираем Создать CylinderMesh.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

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

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

Выставляем значения Top Radius и Bottom Radius на 0.3, а значение Height меняем на 0.15. В результате у нас из цилиндра получается замечательная шайба.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

Кроме того, предполагается, что монетки будут висеть в воздухе на ребре, чтобы студенты могли наконец подготовиться к сессии. Поэтому в рабочем пространстве берёмся за полукруг вращения возле модели монетки, зажимаем Ctrl для дискретности и поворачиваем монетку вокруг оси Z на 90 градусов.

Фото в цвете
Фото в цвете

Теперь дело за формой: добавляем дочерний узел CollisionShape3D, в Инспекторе в свойстве Shape выбираем Создать CylinderShape3D, далее кликаем на эту строчку и в раскрывающемся списке выставляем значение Height, равное 0.15, и значение Radius, равное 0.3.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

Затем с помощью мышки и клавиши Ctrl поворачиваем CollisionShape3D вокруг оси Z до полного совпадения с моделью монетки, то есть на 90 градусов.

Бинго!
Бинго!

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

Сохраняем результат.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

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

Итак, добавляем файл кода в сцену coin.tscn, все настройки оставляем по умолчанию.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень
#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

По аналогии с предыдущим уроком добавляем в код константу ROT_SPEED для хранения числа градусов угла, на которые монетка должна поворачиваться каждый кадр (которых у нас по умолчанию 60 в секунду), а в функцию _process() добавляем функцию вращения rotate_y(), в которой размещаем функцию для перевода градусов в радианы deg_to_rad(), в которой размещаем константу ROT_SPEED.

extends Area3D const ROT_SPEED: int = 2 # Число градусов угла, на которые монетка должна поворачиваться каждый кадр # Called when the node enters the scene tree for the first time. func _ready() -> void: pass # Replace with function body. # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta: float) -> void: rotate_y(deg_to_rad(ROT_SPEED))

И не забываем про комментарии.

Вот картинка, если вам так удобнее
Вот картинка, если вам так удобнее

Переходим во вкладку level_1.tscn, к корневому узлу Level1 инстанцируем файл coin.tscn и размещаем монетку где-нибудь не в плоскости пола.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень
Воооооон она, маленькая (как моя зарплата)
Воооооон она, маленькая (как моя зарплата)

Посмотрим, что получилось.

Монетка есть, монетка вращается, но не исчезает при касании. Значит, пора это дело исправлять.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

Задач состоит в следующем: при касании игровым персонажем монетки последняя должна удалить сама себя из уровня. За проверку на касание с другим объектом отвечает функция has_overlapping_bodies(), а за удаление объекта из игры - функция queue_free(). Тогда мы можем изменить функцию _process() следующим образом:

func _process(delta: float) -> void: rotate_y(deg_to_rad(ROT_SPEED)) if has_overlapping_bodies(): queue_free()

(Queue означает выстраивание в очередь, потому что удаляемый объект буквально встаёт в очередь на удаление. При удалении объекта происходит освобождение памяти, выделяемой для этого объекта - поэтому у функции queue_free() есть приставка free. Само удаление проходит за один кадр, то есть за один проход функции _process(), что при 60 FPS займёт 1/60 секунды.)

Посмотрим, что у нас получилось в итоге.

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

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

Если мы в сцене coin.tscn в панели Сцена выберем корневой узел Coin...

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

а затем в панели Инспектор перейдём на вкладку Узел, то увидим список сигналов, доступных для обработки.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

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

Присоединяем как есть
Присоединяем как есть

После нажатия кнопки Присоединить нас перекидывает на файл с кодом coin.gd, и мы видим перемены к лучшему:

func _on_body_entered(body: Node3D) -> void: pass # Replace with function body.

Кроме того, в панели Сцена у корневого узла Coin появилась иконка быстрого доступа на панель сигналов.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

Если кликнуть на неё, автоматически откроется вкладка Узел в панели Инспектор, где так же отмечен факт наличия обработки сигнала.

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

Ну и нам остаётся только добавить в обработчик сигнала функцию для удаления объекта в случае пересечения области другим объектом. И да, предыдущий код, использующий функцию has_overlapping_bodies(), нужно закомментировать, чтобы он не влиял на игровой процесс (можно выделить эти две строчки и нажать Ctrl+K).

Вот что у нас получается с кодом в итоге:

extends Area3D const ROT_SPEED: int = 2 # Число градусов угла, на которые монетка должна поворачиваться каждый кадр # Called when the node enters the scene tree for the first time. func _ready() -> void: pass # Replace with function body. # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta: float) -> void: rotate_y(deg_to_rad(ROT_SPEED)) #if has_overlapping_bodies(): #queue_free() func _on_body_entered(body: Node3D) -> void: queue_free()

Вот как оно выглядит:

#5 Godot: 3D Platformer от BornCG. Добавление монетки на уровень

И вот как оно играется:

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

Иначе буду смотреть на вас вот так. Это Голден Бой, кстати
Иначе буду смотреть на вас вот так. Это Голден Бой, кстати
6
12 комментариев