November 5, 2022

Ходьба сквозь юнитов в Godot

Моя игра будет хоть и с видом сверху, но не совсем сверху - персонажи будут отображаться боком. Хз как это называется (точно не изометрия), но как в вампирах, в общем

Соответственно, задача - дать возможность юнитам заходить за других юнитов

Перед тем как разъясню решение, немного базы

Про коллизии

В Godot есть встроенная физика. Мне ещё предстоит протестить её возможности в полной мере, но базовые вещи в ней работают и пока этого достаточно

Как оно работает:

  1. Создаём ноду KinematicBody2D, назовём её Unit
  2. Кладём в неё ноду CollisionBody2D и рисуем какую хотим коллизию
  3. Теперь открываем настройки KinematicBody2D и в коллизии у нас есть две непонятные херни с цифрами

Из доки было непонятно, что это и как оно работает, но суть такова.

  • Layer - это то, в каком слое находится наш объект
  • Mask - это слои, с которыми наш объект будет сталкиваться

Если два объекта не сходятся (Layer одного не находится в Mask другого), то они друг друга игнорируют

Дабы не забыть, какая цифра за какой слой отвечает, можно сходить в Project -> Project Settings -> Layer Names -> 2D Physics и прописать цифрам названия. Цифрами они отображаться, к сожалению, не перестанут, но зато при наведении будет видно, что это

Движение

После создания коллизий, можем двигать юнита, навесив на него простой скрипт:

class_name SomeUnit
extends KinematicBody2D

var speed = 400
var direction = Vector2.LEFT
var velocity = Vector2.ZERO

func _physics_process(delta):
  velocity = speed * direction
  move_and_slide(velocity)

Так юнит будет двигаться в направлении direction со скоростью speed

Для движения есть разные методы.

Например, move_and_slide автоматически сдвинет юнита за пределы чужого коллайдера, если он окажется в нём. Ну например, мы делаем дэш, который отключает юниту коллизию, и он заканчивается до того, как мы "выйдем" из юнита. Эта штука автоматом его вытащит.

move_and_collide, соответственно, упрёт юнита в препятствие и не будет пропускать сквозь.

Есть и ещё. Перечислять всё не буду, подробнее можно почитать тут: тык

Разумеется, прописывать каждому движущемуся юниту всё это каждый раз такое, поэтому я запилил дефолтный класс.

Делаем юнитов

В рамках своих хотелок сделал пока что 4 слоя: Environment, Body, Hitbox, Hit

А юнитам - два коллайдера

  • Один кружком в нижней части юнита. Это типа точка, где юнит "стоит". Его нужно будет обходить. Это будет Layer Body + Mask Environment, Body
  • А второй будет размером с модельку. Он будет отвечать за получение урона. Это будет Layer Hitbox + Mask Hit

Кружок навесил на сам KinematicBody2D, чтобы логика ходьбы считалась от кружка. А второй коллайдер вложил внутрь в Area2D и назвал её CollisionHitbox

Получилось вот так:

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

Подумал, что щас будет куча геморра с тем, чтобы трекать координаты каждого персонажа и поднимать спрайты одного выше другого, ведь у меня же тут не 3D...

Оказывается, всё гораздо проще, и в движке это уже реализовано по дефолту.

Есть специальная нода YSort. Нужно всего лишь положить юнитов в неё, и они автоматически отсортируются. Кайф

Не думаю, что это специфичная фича для Godot, в других движках наверняка тоже есть, уж больно критичная фича

Получилось как-то так:

С коллизиями: