Как я учил манекен паркуру: от телепорта к Motion Matching
Привет. Я учусь геймдеву, C++ и Unreal Engine осваиваю прямо по ходу первого серьёзного проекта. Делаю прототип паркур-шутера от первого лица, референс The Finals: спринт, слайд, мантл, вольт, и всё это на скорости. Человек из индустрии, который иногда тыкает меня носом в правильную сторону, сказал: «веди блог, собирай мнения». Ну вот, веду. Это первый пост, он про то, как механика, которая работала, наконец стала выглядеть.
Что было
Вся логика движения у меня живёт в собственном UParkourMovementComponent, наследнике CharacterMovementComponent. Не блюпринт-макароны, а честный C++, который я писал (и переписывал) руками, разбираясь в каждой строчке:
- детекция стен и уступов капсульными трейсами, с веером направлений, чтобы хватать уступы и под углом;
- мантл и вольт как кастомные режимы движения (PhysCustom) с фазами: отойти, подняться, заехать;
- слайд с собственной физикой в CalcVelocity: буст на входе, затухание, запрет разгоняться зажатым W;
- дак-вольт в низкие проёмы, койот-тайм, джамп-буфер и вся эта мелочь, из которой складывается «приятно».
Механически это работало неплохо. Визуально персонаж просто телепортировался по фазам с анимацией прыжка. Смотреть на это было больно.
Решение: GASP
GASP (Game Animation Sample) - это бесплатный проект Epic с ~800 анимациями и настроенным Motion Matching: системой, которая не переключает анимации по состояниям, а каждый кадр ищет в базе кусочек анимации, лучше всего подходящий под текущую позу и траекторию.
План был простой: «прикручу за вечер». Ха.
Грабли, о которые я убился
1. Live Coding не переживает перезапуск редактора. Патчи Ctrl+Alt+F11 живут до закрытия редактора, после рестарта поведение «откатывается» к старому бинарю. Я дважды «терял» уже починенные фичи, пока не понял, в чём дело. Правило: рестартанул редактор со свежим C++, значит собери заново.
2. У чузер-таблиц есть «паспорт», класс контекста. Скормил таблице не тот AnimInstance - получи красный лог на каждый кадр. Лечится сменой Context Data в ассете и перепривязкой колонок.
3. Дубликат строки в чузере - сиамский близнец. Duplicate копирует не вложенную под-таблицу, а указатель на неё. Правишь копию, а ломаешь оригинал. Строку для слайда пришлось собирать с нуля.
4. PoseHistory - не опция. Чузер монтажей с колонкой «подбор позы» без подключённой истории поз молча возвращает пустоту. Никаких ошибок, просто ничего.
5. Направление движения пришлось писать самому. Мой персонаж на классическом CMC, а направленная локомоция (бег спиной, приставные) в GASP заточена под новую систему Mover. Ковырял бинарники ассетов грепом (реально), вытащил их пороговую логику с гистерезисом (±60° вперёд, ±120° назад, в страйфе жёстче) и переписал в свой компонент на C++. Двадцать строк, а спина ожила.
Кульминация: мантл через Motion Warping
Вместо моих ручных фаз движение на препятствие теперь ведёт монтаж с root motion, а Motion Warping подтягивает анимацию так, что ладони ложатся ровно на кромку, которую нашёл мой детектор. Выбор монтажа делает чузер по метрикам препятствия и текущей позе: система сама решает, с какой ноги лезть и с какого кадра стартовать. На скорости берёт вариант с разбега, из стойки - спокойный подъём.
Что ещё не работает (честно)
- Вольт-чузер возвращает пустоту. По всем колонкам запрос проходит, а строки нет. Завтра иду в живой дебаг чузера, буду смотреть, на чём гаснет.
- «Шаги в пустоту»: у анимаций заложен подход к препятствию, и вплотную к стене персонаж топчет воздух. Частично лечится выбором stand-вариантов.
- Залаз в вентиляции (низкие проёмы) идёт без анимаций: у GASP просто нет монтажей «залезть в нору». Думаю в сторону IK.
Дальше
Добить вольты, перенести вайтбоксы в красивое окружение (Electric Dreams от Epic уже мигрирован: 44 ГБ, отдельная история с OOM и файлом подкачки), и камера от первого лица по-взрослому: скрытие головы, наклон корпуса, отъезд в слайде.
Вопрос к тем, кто женил кастомный CMC с GASP: какие грабли словили вы? Особенно интересно, кто как решал направленную локомоцию и траверсал. Пишите, соберу вторую часть с чужими шишками.
Жду любую критику, я здесь за этим