Трудности разработки движения Medieval Contrast
Приветствую, читатели Пикабу! На связи главный разработчик и проектный директор Medieval Contrast Максим Дегтяренко. Отвечая заранее, да, этот пост в том числе для того, чтобы привлечь внимание к нашему проекту, но в основной своей части хотим рассказать о трудностях, с которыми сталкиваемся при разработке.
Многие читатели не любят воду в подобных статьях, поэтому в этот раз мы хотим добавить информативности.
Над чем мы работаем сейчас?
Самое основное, что не маловажно в играх от третьего лица - это движения и анимации персонажа, в данном случае речь идёт о самых базовых механиках. Казалось бы, Unreal Engine сделал всё для того, чтобы такие системы реализовывались в кратчайшие сроки и максимально просто, но не в нашем случае.
Изначальная задача - разделить movement-системы на стандартную и прицеливание/бой, а так же создать эту movement-систему для режима прицеливания. Ключевым фактором, усложнившим разработку, стало наше желание сделать переход между режимами плавным.
Мы начали с выставления камеры для каждого из режимов, руководствуясь в том числе правилом третей, а так же реализовали плавный переход между ними через TimeLine.
Расположение камеры в режиме прицеливания/боя. Камера немного ближе и выше. FOV 90 градусов для наилучшего обзора.
Данной "системой" мы не хвастаемся, это достаточно легко реализуемо, но хотим показать с чего начинали.
Переход к Movement-системе
Default движения персонажа мы оставили стандартными как в шаблоне Unreal Engine 5 для TPS-игр, но внесли некоторые корректировки в значения Movement-компонента: уменьшили максимальную скорость, скорректировали разгон персонажа, сделали режим спринта и прицеливания (изменение скорости) - всё это, конечно же, с репликацией под кооператив.
Немного о дефолтной системе (если кто-то не знает как она работает): персонаж поворачивает своё тело в зависимости от WASD/inputs, направление движения так же корректируется с ориентацией на расположение курсора/контроллера.
Самым важным по прежнему оставалась система передвижения при прицеливании или в режиме боя. Многие разработчики и YouTube-авторы могут сказать: чего тут сложного? Вроде включить Use Control Rotation Yaw (далее - UCRY) и выключить Orient Rotation to Movement и готово, но такая система имела бы сразу несколько ключевых минусов:
Даже с корректной настройкой UCRY моментально поворачивает персонажа в сторону камеры.
Переход между двумя режимами слишком резкий и банальная интерполяция при переходе от режима к режиму не даст нужного результата, равно как при резком повороте мыши в обратную сторону - персонажа просто мгновенно повернёт без плавного поворота.
Короче говоря, такая система слишком проста и вместе с этим совершенно нереалистична с точки зрения погружения в игровой мир.
Мы начали искать референсы Movement-системы среди игр, созданных на Unreal Engine, и нашли. Подходящая по своей логике система Movement-режимов присутствует в игре Bellwright, буквально то, что нужно и нам: верх тела поворачивается за курсором, ноги при повороте (при большом угле) плавно догоняют тело.
Сделать такую систему для локальной игры достаточно просто, но данная система должна работать непрерывно во время сетевой игры, а значит: отдельное внимание репликации (синхронизация - простыми словами) и сетевой оптимизации.
Сама логика не такая сложная с точки зрения нагрузки, поэтому было решено "вешать" её сразу на Event Tick, так как Unreal Engine оптимизирует выполнение сервером Set Actor Rotation (применение поворота персонажа на сервере) и не даёт ему исполняться каждый тик (она работает в районе 10-20 раз в секунду вместо привязки к FPS игрока, который может доходить до безумных 100-120 в секунду, что просто "убивало" бы сервер). Проблемой так же является то, что применение Set Actor Rotation на сервере в Unreal Engine почему-то синхронизируется между сервером и всеми клиентами КРОМЕ самого игрока, которого сервер поворачивает. Вроде ты даёшь серверу команду повернуть себя, но этот поворот срабатывает везде кроме тебя самого.
Как можно было решить данную проблему?
Первое что приходит в голову - сделать Multicast-событие, которое и будет поворачивать персонажа сразу для всех, включая игрока-владельца, но такая система:
Создаёт высокую нагрузку на сервер, так как Multicast-событие каждый тик клиента - очень дорого для производительности.
Неминуемо создаёт задержку в исполнении из-за сетевой задержки, что при очень плохом ping может создавать задержку в 5-10 секунд перед лишь одной итерации поворота (в случае с интерполяцией - таких нужно много), т.е персонаж будет постоянно "дёргаться" как для сервера, так и для клиентов.
Интерполяция - плавное изменение значения от изначального до целевого с заданной скоростью. Например, при стартовом значении 0 и целевом 1 интерполяция создаёт ряд последовательных значения вроде: 0.001, 0.005 .... 0.995, 1.
В связи с этим лучше всего оставить Set Actor Rotation на сервере (для сервера и других клиентов), так как это часть Character Movement, которая реплицируется автоматически и не создаёт такой нагрузки, но при этом создать некоторый prediction, когда сначала клиент делает поворот у себя, а потом отправляет его на сервер, не дожидаясь ответа от сервера. Таким образом клиент видит у себя плавный поворот без задержек, а сервер уже потом подтверждает этот поворот у себя и транслирует его другим клиентам так же плавно.
При этом особенность системы такая, что для клиента мы используем локальные переменные, а не реплицируемые для других клиентов (в основном для анимаций), что как раз-таки даёт плавность для самого клиента.
Сейчас мы всё ещё сталкиваемся с проблемами данной системы и ищем решение, если точнее: когда нужно начать поворот персонажа - он срабатывает через 1-2 секунды, что является заметной задержкой, так же скорость интерполяции вращения у хоста (он же игрок, он же сервер) и на клиенте отличаются - у хоста скорость интерполяции корректная, у клиента - сильно замедлена, хотя, казалось бы, у клиента вычисления происходят локально и таких задержек происходить не должно, как и такой разницы в скорости интерполяции.
Под конец хочется добавить, что вся разработка ведётся без огромной опытной команды, учимся на своих ошибках и медленно, но верно идём к требуемому результату.
Надеюсь, в этот раз не будет большой критики и осуждению наших публикаций, всё-таки стараемся делиться нашей работой над игрой как есть, а там уже как получается. Тем не менее будем рады любой объективной информации касаемо системы - для нас это не маловажно, мы читаем и учитываем всё, что вы пишите и всегда даём вам обратную связь. Спасибо!




