В прошлой статье было небольшое введение в принцип работы Bolt и основы визуального программирования. В этой же попробуем небольшой платформер сделать и затронем основные аспекты.
Поработаем с силами, рейкастами и затронем тему модульности и переиспользования компонентов.

Цикл статей
- Введение
- Платформер — движение, анимация, рейкасты
Туториалы можно скачать бесплатно с Гитхаба. Каждый туториал будет отдельной папкой в проекте. Конкретно этот туториал будет лежать в директории Assets/Tutorials/02 — Platformer.
Оглавление
Подготовка
Накидаем простенькую сцену с платформами и шипами. Я использовал редактор Тайлов, но это могут быть и обычные спрайты с колайдерами на них. Добавляем в иерархию GameObject
игрока.

В инспекторе добавляем к объекту компонент Flow Machine.

Нажимаем Edit Graph и приступает к настройке графа.
Движение персонажа
Мы хотим, чтобы персонаж двигался влево и вправо в зависимости от горизонтальной оси ввода, для которой предварительно настроены сочетания клавиш A и D на клавиатуре или левый джойстик на контроллере. Когда вы идёте влево, возвращается -1
, а когда вправо, то +1
. Скорость движения зададим в переменной.
Для начала можно зайти в настройки и задать необходимое управление в Edit > Project Settings > Input Manager, если не нравятся дефолтные настройки.

Теперь добавим переменную, откуда будем читать скорость. В инспектора объекта игрока добавим переменную в компоненте Veriables. Подробнее можно почитать в документации.

Далее, собственно, нам нужно получить горизонтальную ось ввода. Мы можем сделать это с помощью блока Get Axis.
- Добавляем узел Codebase > Unity Engine > Input > Get Axis.
- Вводим «Horizontal» (зарезервированное слово).
Новый узел будет полупрозрачным. Так Unity нам сообщает, что они пока нигде не используется. Этот эффект можно отключить на панели отжав пункт Dim.

Далее мы хотим на основании инпута вычислить итоговое значение скорости. Для этого нужно считать значение переменной Speed, созданной ранее, перемножить со значением с инпута и записать в новую переменную.
- От выхода узла GetAxis тащим указатель и создаём узел Multiply.
- От второго входа нового узла тянем указатель и создаём узел для чтения переменной Variables > Object > Get Object Variable.
- Выбираем там Speed.
- Выход узла Multiply соединяем с новым узлом Variables > Graph -> Set Graph Variable.
- Дадим новой переменной имя Movement.
- Соединяем управляющий вход с Update.

Каждый кадр (событие Update) мы получаем значение инпута по оси X в диапозоне [-1;1], домножаем на скорость и сохраняем в переменную Movement.
Теперь нужно задать компоненту Rigidbody 2D персонажа скорость. Скорость состоит из двух составляющих — по осям X и Y, которые пакуются в Vector2
.
- Добавляем узел Get Variable для получения Movement из предыдущего шага.
- Это передаёт на вход X координате новой ноде Vector2.
- А в Y передаём считанную Y координату из текущей скорости (Codebase > Unity Engine > Rigidbody 2D -> Get Velocity).
- Полученный вектор передаём на вход узлу для установки нового значения скорости (Codebase > Unity Engine > Rigidbody 2D -> Set Velocity).

Остаётся теперь связать выход узла по установке Movement со входом узла, устанавливающего скорость.
Ну и заодно сгруппируем узлы. Это делается выделением с зажатой клавишей Ctrl.

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

Даже при движении влево спрайт всё равно смотрит вправо. Хорошо бы это исправить. Для этого будет инвертировать значение скейла по оси X.
- Если двигаемся вправо (значение Movement больше 0), значение Scale должно быть положительным.
- Если движемся влево (значение Movement меньше 0), значение Scale должно быть отрицательным.
- Если стоим на месте (значение Movement равно 0), то ничего не делаем.
Значение Y и Z оставляем как есть.

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

Но сейчас персонаж просто скользит. Хотелось бы сделать анимацию движения.
Анимация
Возьмём готовые спрайты и анимации из примера проекта Penny Pixel.
Если открыть аниматор, то нас там пока интересует всего 2 анимации: айдловая и бега. Если посмотреть на переход для анимации бега, то заметим, что оно зависит от скорости по оси X. Нам всего лишь нужно это значение передавать аниматору.
Ну и флажок grounded пока в true выставим, чтобы не запускалась анимация полёта.

На графе, как я уже сказал, нам нужно всего лишь передать значение скорости аниматору и записать в переменную velocityX.

Остаётся только соединить с остальным графом и запустить.

Следующим шагом добавим возможность прыгать.
Придание силы объекту для прыжка
- Добавим глобальную переменную для силы прыжка и назовём JumpPower.
- Events > Input, а там On Button Input.
- Вместе со считанной силой прыжка передаём в Codebase > Unity Engine > Rigidbody 2D > Add Force (Force, Mode).

Если запустить, то по нажатию на Space персонаж будет прыгать. Но есть одна проблема…

Во-первых, нету анимации прыжка. Во-вторых, если, находясь в воздухе, снова нажать Space, то персонаж прыгнет ещё раз. А нам бы этого не хотелось.
Создадим новый слой и назначим его платформам.

Необходимо дописать логику по проверке, находится ли персонаж на земле. Один из способов — рейкасты. Будем выпускать лучи вниз на небольшое расстояние, чтобы проверить, сталкиваются ли они с платформой. Проверять как раз по этому слою будем.
Переиспользование логики
Так как эта проверку нужна в двух местах, то выделим всю логику в отдельный юнит Assets > Create > Bolt > Flow Macro.
Для каста использовать будем CircleCast
. Хотя это не так принципиально.
- Получаем позицию текущую.
- Делаем рейкаст по слою Platform, направляя вниз с радиусом 2 и дальностью 2.
- Результат получаем в отдельную ноду.
- Делаем проверку на то, было ли столкновение.
- Результат проверки столкновения луча с платформой пишем в bool переменную IsGrounded на выход.

Теперь в графе персонажа нужно немного изменить узлы, обрабатывающие прыжок.
- Перетаскиваем на граф персонажа граф GroundCheck.
- Добавляем ветвление.
- Соединяем с узлом, задающим силу.

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

Точно также добавим проверку к анимации.

Теперь, если персонаж в воздухе, будет проигрываться соответствующая анимация.

Хотелось бы как-то визуализировать дебажную информацию для отладки.
Отладка и OnDrawGizmos
Было бы неплохо как-то визуализировать рейкаст. Как минимум, хочется видеть, как далеко он бьёт. Для этого можно воспользоваться событием OnDrawGizmos
, которое позволяет в редакторе рисовать отладочную информацию.
В нашем случае просто будет рисовать линию. Увеличим дальность луча до 50 и добавим соответствующие узлы для отрисовки линии.

- Вытаскиваем информацию о текущем положении.
- Создаём Vecto3 на основе X и Z координаты. Эта позиция будет конечной при отрисовки линии.
- Рейкаст мы делаем вниз, поэтому в Y координату вписываем Y координату текущего положения персонажа с за вычетом дальности луча.
- Добавляем ивент, который каждый кадр рисует информацию.
- На вход узла по отрисовке в начальную координату передаём текущее положение персонажа, а конечной точкой будет новое вычисленное значение позиции.
Ещё нужно в Scene View включить отображение Gizmos.

Теперь запустим и посмотрим.

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