Итеративный дизайн боевой системы тактической RPG и его реализация в UE4
Илья Руднев — геймдизайнер Shores Unknown — рассказывает, как ему удалось собрать на блупринтах логику для тактической RPG.
Shores Unknown — синглплеерная тактическая RPG в стилизованном фэнтезийном сеттинге на Unreal Engine 4. Игра разрабатывалась в течение 5 лет и в настоящий момент вышла в ранний доступ на площадках Steam и GOG. Прохождение текущей версии Shores Unknown займет у игрока примерно 20 часов геймплея.
История моей игры началась с учебного проекта, который я разрабатывал во время изучения Unreal Engine 4 и блупринтов. Со временем масштаб прототипа вырос, и в конечном итоге его разработка превратилась в полноценное производство мультиплатформенной игры (со стабильной сборкой на Nintendo Switch), созданной небольшой командой энтузиастов из разных стран.
На старте проекта я был единственным геймдизайнером и программистом в коллективе и отвечал как за концепцию игры, так и за ее воплощение с помощью систем и функционала в UE4. Поскольку это был мой первый опыт разработки полноценной RPG с нуля на Unreal, на каждом этапе я открывал для себя что-то новое.
Данный кейс посвящен истории создания, прототипирования и дальнейших итераций ключевого геймплея Shores Unknown, которым я очень горжусь, и ради которого не побоялся рискнуть.
Это пошаговая боевая система без разметки игрового поля. Персонажи сами передвигаются, выбирают направление атаки и (в некоторых случаях) автоматически применяют способности, избавляя игрока от микроменеджмента. При этом на протяжении всего приключения внимание пользователя сосредоточено на стратегическом принятии решений и адаптировании своей тактики к зачастую непростым стычкам с разнообразными врагами.
Источники вдохновения и первые шаги
При разработке Shores Unknown я вдохновлялся боевыми системами классических западных и японских RPG.
В частности, большое влияние на формирование будущего проекта оказали ролевые игры на движке Infinity Engine (серия Baldur’s Gate); геймплей World of Warcraft вдохновил на использование модели ролевого треугольника в формате «Танк — Лекарь — ДД», а также на ряд других механик, например, системы баффов и дебаффов персонажей.
Но самым большим источником вдохновения стала The Last Remnant — игра, которая и побудила меня разработать собственную боевую систему. Это довольно экспериментальная JRPG с необычной механикой сражения боевых отрядов, изданная Square Enix в 2009 году. Вместо того, чтобы по отдельности управлять каждым персонажем на поле боя, игрок давал общие приказы своим войскам, после чего каждый отряд автоматически перемещался и на протяжении хода выполнял действия на основе отданного приказа, класса и характеристик персонажа.
Приступив к изучению UE4, первым делом я поставил себе цель разобрать боевую систему The Last Remnant и создать ее аналог на блупринтах, придерживаясь следующих правил:
- в распоряжении игрока может быть до 5 групп («отрядов»), и до 5 персонажей в каждой из них;
- во время сражения игрок отдает приказы каждому отряду, а не каждому персонажу по отдельности, выбирая цель, тип действия (атака, поддержка и так далее) и количество так называемых «очков подготовки» (ОП), которые каждый персонаж может использовать в порядке очереди;
- на стадии приказов бой ставится на паузу, поэтому игрок может спокойно оценить ситуацию и продумать стратегию;
- в начале сражения персонажи автоматически встают в одну или несколько очередей, которые определяют ход боя: герои в одной очереди выполняют свои действия один за другим в порядке скорости, в то время как несколько очередей проходит одновременно, но в пределах одной очереди действует только один персонаж;
- как только все персонажи заканчивают свои действия, игра переключается на следующую фазу приказов. Если одна из сторон побеждает — бой окончен.
Кроме того, на действия персонажей в бою влияют следующие механики:
- у каждого героя есть слоты для экипировки оружия и инвентаря, а основной набор способностей зависит от снаряжения. Например, персонаж с двуручным мечом использует способности из ветки талантов владения двуручным оружием, а танк со щитом — из ветки оборонительной стойки;
- некоторые типы способностей, в частности, магические заклинания, не привязаны к конкретной экипировке, и герой всегда может их применить;
- по мере использования своих навыков, персонажи постепенно накапливают опыт, увеличивая основные характеристики (Сила, Интеллект и Ловкость), и автоматически изучают новые способности в ветке талантов, на которой они специализируются.
Работая над первой итерацией боя, мне пришлось исследовать, придумать и добавить ряд основных классов акторов:
- Battle Manager (Менеджер боя): отвечает за порядок и изменение фазы боя, спавн отрядов на поле боя и рассчитывает, в каком порядке совершаются действия;
- Union («Отряд»): включает в себя всю логику и переменные, относящиеся к одной группе в бою, то есть определяет тип действий в очереди, общее здоровье группы, скорость и другие агрегированные характеристики, а также общее позиционирование на поле боя. Кроме того, отряды NPC находятся под управлением ИИ, который отвечает за стратегические решения каждой группы;
- Character Actor Class: управляет боевыми характеристиками каждого персонажа, выбирает и генерирует способности во время действий, отслеживает эффекты баффов/дебаффов. У каждого персонажа есть ИИ на основе Behavior Tree, который отвечает за их перемещение во время боя в режиме реального времени.
Примечание. Несмотря на то, что сражения были пошаговыми, в ранних итерациях боевой системы персонажи совершали активные маневры на поле боя в режиме реального времени, даже вне фазы действия. За счет этого достигался эффект масштабных «потасовок». В дальнейшем мы заметно сократили подвижность юнитов, чтобы облегчить игроку понимание картины боя во время фазы приказов.
В итоге взаимодействие между различными акторами боевой системы происходит следующим образом:
В основном, персонажи влияют друг на друга за счет способностей — важного элемента боевой системы Shores Unknown. Вместо того, чтобы приобрести на Unreal Marketplace одно из существующих на тот момент решений, или интегрировать в проект Gameplay Ability System от Epic Games, я разработал собственный фреймворк для способностей. Это решение дало не только ценный опыт, но и обеспечило максимальную гибкость системы в соответствии с моим видением боевой механики игры.
Я хотел, чтобы моя система давала возможность мне или другому участнику нашей команды легко внедрять новые способности по модульному принципу целиком на блупринтах. Планировалось расширение спектра механик, начиная от базовых действий, наносящих урон, и заканчивая масштабными AoE-заклинаниями на поле боя, не говоря уже об исцеляющих эффектах, постоянных баффах группы и призыве приспешников.
Для упрощения работы с большим количеством способностей, имеющих схожие принципы работы, я активно использовал наследование для классов блупринтов: от общих родительских типов («урон», «исцеление» и так далее) до дочерних классов уникальных способностей (например, «Дыхание дракона» — способность из линейки АоЕ-заклинаний). В свою очередь, базовые «урон» и «исцеление» также произошли от класса актора Parent Ability.
Аналогичная иерархия была у акторов баффов. Родительский класс Buff Parent создавал общую логику для всех баффов, которая расширялась в дочерних классах. Чтобы все было под контролем, я установил ряд основных правил взаимодействия между персонажами, способностями и баффами:
- акторов способностей спавнит только актор персонажа и только в порядке, установленном Battle Manager;
- когда способность спавнится и становится активной, она временно получает контроль над анимацией и движением персонажа;
- в одной очереди может действовать только один герой, то есть находясь «под контролем вызванной способности»;
- в целом, баффы спавнятся способностями или другими баффами (в отдельных случаях);
- бафф также может запросить спавн особых способностей-«проков» (proc — Programmed Random Occurrence) у актора персонажа при выполнении определенных условий. При этом тайминг подобного явления контролируется классом Battle Manager, который привязан к состояниям действий персонажа через диспетчера событий. Например, если персонаж уклоняется от атаки, он проведет ответную атаку в конце текущего действия.
Примечание. На ранней стадии разработки в игре был только базовый тип «прока», который запускался строго после завершения действия персонажем. Позже мы расширили эту систему и добавили больше условий для триггера. Например, после смерти персонажа, урона по цели, а также в дополнительные временные промежутки: в начале или в конце фазы действия.
Система «Столкновения»
Несмотря на то, что в Shores Unknown отсутствует аспект позиционирования на поле битвы, характерный для классических пошаговых боевых систем, мне хотелось, чтобы персонажи нападали с фланга, прерывали вражеские атаки и наносили удары в спину своим целям. Эти механики нуждались в косвенной привязке к выборам игрока в сражении. Руководствуясь этими соображениями, я разработал концепцию «Столкновения» (Engagement), которая стала основной боевой механикой.
Ключевые правила Столкновения появились в проекте довольно давно, еще во время работы над первой итерацией боя. С тех пор они практически не менялись. Механика Столкновения предусматривает 3 основных условия, которые должны знать игроки.
- У каждого Отряда может быть от 1 до 3 ячеек Столкновения. Если Отряд атакует в ближнем бою другой Отряд, и у них обоих есть пустой слот, они «сталкиваются» друг с другом. Атаки ближнего боя во время сражения этих Отрядов происходят в обычном режиме, без каких-либо бонусов или штрафов.
- Однако, если у Отряда, который атакуют в ближнем бою, не осталось свободных слотов, он примет атаку с фланга. Затем, при повторной атаке в той же очереди он получит удар с тыла, и, наконец, мощный удар. Атакующие юниты при этом наносят увеличенный урон.
- Кроме того, если Отряду A удается атаковать Отряд B, у которого в очереди также есть приказ на ближний бой отличной от А цели, и при этом имеются пустые слоты Столкновения, происходит Перехват. При этом между A и B возникает Столкновение, и Отряд B вынужден атаковать Отряд А.
Эти правила формируют процесс боя, в котором игроку необходимо отслеживать текущие активные Столкновения; использовать более быстрых юнитов для перехвата целей, которые могут нанести урон слабозащищенным отрядам магов и целителей; фокусироваться на врагах, чтобы увеличить шанс атак с фланга и периодически перебрасывать столкновения с сильными отрядами врага между своими «танками», чтобы лекари успели излечить входящий урон.
Звучит запутанно, но вот простой пример Столкновения во время действий двух дружественных отрядов А и В против враждебного отряда С.
Во время фазы отдачи приказов у всех трех отрядов есть в очереди действие ближнего боя. Отряд A (скорость 12) хочет атаковать враждебный Отряд C. Отряд C (скорость 10) хочет атаковать Отряд B (скорость 9), а он, в свою очередь, тоже нацелен на Отряд C. Ни у одного из Отрядов нет активных Столкновений.
Вот как разрешается ситуация на этапе боевой фазы:
- Поскольку Отряд А самый быстрый, первое действие за ним. Так как Отряд C нацелен на другой Отряд, происходит Перехват.
- Теперь Отряды A и C столкнулись. C атакован A, как и планировалось.
- Отряд C ходит следующим. Так как его перехватил Отряд А, он использует действие ближнего боя на А, а не В.
- Последний ход за Отрядом B. Поскольку у C больше не осталось слотов столкновения после перехвата A, действия B против него вызывают атаку с фланга, наносящую дополнительный урон.
Важно отметить, что как только начинается фаза боя, приказы каждому Отряду фиксируются: игрок больше не может их изменить. Вместо игры на реакцию на первый план выходят стратегическое мышление и способность планирования на несколько шагов вперед.
За кадром актор Battle Manager берет на себя все, что связано с боевой фазой, включая формирование очередей, отсортированных по значениям скорости, а затем реализует действия каждого отряда до тех пор, пока каждый живой персонаж в отряде не использует способность в соответствии с выбранным действием (выбор конкретной способности каждый персонаж при этом совершает автоматически).
Пример реализации логики Столкновения в виде функции в классе BattleManager:
Пре-альфа и вторая итерация боя
После нескольких месяцев работы над игрой, которая на тот момент называлась Project Shore, у меня появился рабочий прототип боевой системы с ассетами-плейсхолдерами. В это же время состав команды вырос до 3 человек: к проекту присоединился левел-дизайнер и сценарист.
На внутренних тестах сражения выглядели довольно интересно. Поэтому мы приступили к созданию преальфа-версии, рассчитанной примерно на час геймплея. В нее мы включили самое начало запланированной сюжетной линии и несколько сражений, которые различались по масштабу и уровню сложности. Помимо скриптинга всех сюжетных событий и геймплея вне боя, а также разработки и настройки боевых энкаунтеров, я поработал и над созданием интерфейса с использованием UMG UI Designer.
К концу 2017 года, после нескольких внутренних тестов, мы выпустили преальфа-версию игры на Indiedb и Gamejolt, где также выходили серии моих девлогов.
Project Shore тепло приняли игроки, и ее заметил портал AlphaBetaGamer, который записал летсплей с полным прохождением билда и опубликовал статью о первых впечатлениях от игры на своем сайте. Также сборка привлекла внимание нескольких издателей.
Обратная связь от игроков и паблишеров, а также наблюдения за тем, как наша аудитория проходит игру на стримах, заставили меня пересмотреть некоторые решения, принятые на ранних этапах разработки, и сделать следующие выводы:
- из-за большой очередности во время боя масштабные сражения превращались в хаос, а действия отдельных персонажей не считывались, даже если поставить сражение на «Паузу» (опция останавливает бой в любой момент);
- приказы общего назначения для отрядов в духе The Last Remnant приводили к непредсказуемым результатам. Не было гарантии, что персонаж применит способность, которую ждал от него игрок;
- шкала морали (примерный аналог общей шкалы здоровья), формации, баффы и различные бонусы за «техничную» игру (например, те же атаки с фланга) перегружали игру механиками и переменными, которые не могли отследить большинство игроков.
Поскольку на тот момент я уже рассматривал Shores как коммерческий проект, то поставил перед собой цель разработать следующую итерацию боя без вышеописанных трех проблем. При этом было важно сохранить концепцию увлекательной тактики без микроменеджмента и ощущение от управления собственным отрядом уникальных наемников, подстраивающихся под стиль игры каждого пользователя.
После тщательного тестирования и консультаций с нашим издателем, я отказался от концепции множественных отрядов, сократив максимальное количество персонажей под контролем игрока до пяти героев. Это кардинальное изменение приблизило игру к классическим партийным RPG, и предоставило игроку гораздо больше контроля, поскольку вместо общих команд теперь можно было выбирать способности, используемые персонажами напрямую.
В результате игрокам стало намного легче следить за ходом боя. Теперь все действия ставились в одну очередь, и персонажи всегда отдавали приоритет способности, выбранной игроком на этапе отдачи приказов.
При этом у героев осталась частичная возможность действовать самостоятельно, так как я сохранил автоматическое позиционирование на поле битвы и механику Столкновения. Просто теперь взаимодействие происходило между отдельными персонажами, а не Отрядами.
За кадром актор Union все еще отвечал за спавн персонажей, расчеты ИИ для принятия стратегических решений и общее позиционирование юнитов на поле боя. Нельзя не отметить, что прямое управление одиночными персонажами значительно упростило и логику в блупринтах.
С этим изменением у меня появилось больше свободы в разработке новых способностей. Теперь и игрок, и искусственный интеллект могли контролировать выбор действий, что дало мне возможность разрабатывать и внедрять эффекты, которые оказывали значительное влияние на поле боя. В прежнюю модель боевой системы такие решения не вписывались из-за массовых стычек и невозможности напрямую атаковать противников, кастующих мощные AoE-заклинания. Теперь такой тип врагов был приоритетной целью. Также игроки получили несколько новых способностей, позволяющих обернуть ситуацию на поле боя в свою пользу: замедляющие эффекты, таунты, щиты с поглощением урона и пассивные навыки, которые можно было включать или отключать в зависимости от ситуации.
В новой версии я также отказался от шкалы морали и формаций. После изучения поведения игроков и их фидбека, я понял, что эти механики не сильно влияли на бой, и внимание пользователя лучше сосредоточить на других системах, вроде баффов или таймлайна действий.
Работа с Excel и управление данными
Исходя из предыдущего опыта работы геймдизайнером в коммерческих проектах, я хотел по максимуму использовать таблицы данных Excel в своей игре, особенно в аспектах, связанных с цифрами и балансом. К счастью, UE4 дал мне возможность связать рабочие процессы в Excel с движком за счет ассетов DataTable. Используя VBA-макросы в Excel, я организовал экспорт данных в формат .csv, для дальнейшего импорта в UE4 и внутриигрового использования.
Это решение оказалось достаточно эффективным для работы с балансом энкаунтеров сражений и детальной настройкой способностей персонажей. Все взаимодействия между героями в бою основаны на числовых показателях, и, учитывая масштаб сражений (а на тот момент одновременно могли сражаться 20-50 юнитов), требовался способ быстрой настройки сразу нескольких значений.
Акторы персонажей, способностей и баффов автоматически «подтягивали» необходимые значения из таблиц в соответствии с присвоенным каждой сущности уникальным ID. Изначально для ID я использовал обычную FName переменную, но позже реализовал в редакторе кастомную логику на C++ для создания выпадающих списков всех основных переменных из таблицы данных, в опциях редактора и нодах, чтобы уменьшить вероятность человеческой ошибки и ускорить рабочий процесс.
Проблемы и решения
Конечно, во время разработки возникало немало трудностей, начиная от банальных ошибок на этапе изучения UE4, связанных с некорректными ссылками ‘access none’, и заканчивая профилированием и оптимизацией логики для стабильной работы на Nintendo Switch.
Примерно через год после начала разработки я столкнулся с серьезным багом в движке, связанным с реализованными в BP struct’ами. Из-за него я потерял несколько дней работы и был вынужден перенести все основные структуры данных игры на C++, что потребовало множество изменений в классах акторов, включая необходимость пересоздавать базовые классы для персонажей и способностей на C++. Но, в конце концов, все это пошло игре только на пользу, и теперь расширять логику и новый функционал стало намного проще (и безопаснее).
Еще один интересный случай связан с «крашами» игры на Switch во время загрузки больших уровней. Изучая причины крашей, я обнаружил, что использование мной по неопытности механизма жестких ссылок (hard references) для ассетов вместо мягких в некоторых таблицах данных приводило к тому, что каждая отдельная модель персонажей игры находилась в памяти 100% времени, и все это вместе бессмысленно сжирало ~ 500 МБ памяти устройства. Проблему решил рефакторинг с переходом на мягкие ссылки: объем занимаемой таблицей памяти сократился до 7 МБ, тем самым не только устранив краши, но и заметно ускорив загрузку игры.
И, наконец, один из самых забавных багов вылез при работе над логикой выбора способностей персонажей во время действия. Например, у персонажа есть способность исцеления и на этапе отдачи приказов у него стоит в очереди исцеление определенного юнита. Если на момент, когда очередь действий доходит до этого героя, доступных для исцеления целей больше нет (у всех полное хп или, наоборот, все умерли), целитель автоматически переключится на атаку врага, чтобы не тратить действие впустую. И вот однажды поиск валидных целей при переключении действия сломался, и, к нашему удивлению, все лекари в игре внезапно начали практиковать самобичевание.
Подведем итоги
В текущей версии Shores Unknown игроку доступны:
- более 50 сражений с различными противниками, начиная от бандитов и заканчивая гигантским человекоподобным вороном-нежитью;
- группы численностью до 5 героев с полностью настраиваемым снаряжением, противостоящие враждебным отрядам, состав которых может насчитывать вплоть до дюжины противников;
- 10 различных веток способностей (групп), которые могут изучить игровые персонажи;
- 300 способностей и 200 баффов — от простых ударов мечом до падающих с неба комет, призыва миньонов и ледяной бури с широким радиусом поражения;
- оптимизированная логика игры, которая выдает стабильные 30 fps на Nintendo Switch.
Сложно описать, какие возможности я открыл для себя во время работы с инструментами Unreal Engine 4. Движок помог мне объединить опыт игрока и геймдизайнера, а также применить технические навыки, чтобы воплотить свое видение игры в жизнь. В результате получилась оригинальная боевая система, которая хоть и вдохновлена мастодонтами индустрии, но вполне самобытна и предлагает по-настоящему уникальный опыт.
Вот и еще одно доказательство, что даже одному технически подкованному геймдизайнеру по силам собрать почти всю логику 20-часовой RPG на блупринтах.
Дерзайте!