ТЕХНИЧЕСКИЙ КЕЙС · Архитектура
Эволюция архитектуры боевой системы
Как развивалась архитектура LiveOps-игры: от разрозненных реализаций к общим контрактам и расширяемым механизмам.
Контекст
Архитектура боевой системы развивалась вместе с игрой и регулярным выпуском контента. Новые герои, способности, предметы и боевые сущности добавлялись годами, поэтому удачное решение для одной механики со временем могло стать источником дублирования, неявных зависимостей и расхождения поведения.
Моя задача состояла не в разовой переработке, а в последовательной модернизации существующих решений: предлагать общие абстракции там, где они снижали стоимость развития, и проводить миграции небольшими шагами, не останавливая производство контента.
Архитектурные направления
Единая точка изменений состояния боя
Операции, меняющие состояние боя, исторически были распределены по нескольким базовым классам и защищённым методам. Это создавало дублирование, различия в поведении и мешало повторному использованию логики новыми боевыми сущностями.
Я вёл поэтапную консолидацию таких операций в единый интерфейс IBattleActions: переносил работу с уроном, лечением, маной, энергией, статусами, характеристиками, спавном и действиями акторов. Изменения выполнялись по доменам небольшими итерациями, поэтому текущие задачи по контенту могли продолжаться параллельно с архитектурной миграцией.
До: базовые классы и локальные protected-методы
│
├── разное поведение в похожих сценариях
└── копирование логики новыми сущностями
После: скрипт сущности → IBattleActions → единые правила изменения состояния боя
В результате интерфейс стал единой точкой входа для мутаций состояния и может передаваться в зависимость любому скрипту, которому требуется изменить ход боя.
Общая модель боевых сущностей
Акторы, фишки, обвесы фишек, расходуемые предметы, боевые модификаторы и триггеры имели похожую задачу: нести кастомную игровую логику. Ранее жизненный цикл и создание скриптов для этих типов развивались по отдельности.
Я участвовал в унификации этой модели вокруг базового класса BattleEntityScript, интерфейсов сущностей и фабрик с единым контрактом. Это сделало добавление новой сущности более предсказуемым: вместо создания отдельного жизненного цикла нужно было встроиться в уже известную архитектурную схему.
Выразимые параметры вместо булевых цепочек
Расчёт урона и лечения — одна из самых часто вызываемых частей боевой системы. Когда методы принимают ряд позиционных булевых параметров, их легко перепутать или неверно интерпретировать, а компилятор не всегда может обнаружить ошибку.
Я перевёл эти вызовы на параметр-объекты DamageOptions и HealOptions с именованными свойствами и переиспользуемыми значениями по умолчанию. Это сделало намерение вызова понятнее, снизило риск ошибок при изменении сигнатур и позволило безопаснее развивать общие правила расчёта.
// Условный пример принципа, не код проекта
// Было: порядок флагов легко перепутать.
ApplyDamage(source, target, value, true, false, true);
// Стало: назначение каждого параметра видно в месте вызова.
ApplyDamage(source, target, value, new DamageOptions
{
IsAbility = true,
IsRaw = false
});
Результат
Архитектурные изменения сделали боевую систему более единообразной и расширяемой: меньше дублирования в базовых классах, ниже риск расхождения поведения между сущностями и яснее границы ответственности систем.
Главный принцип этой работы — не заменять всё одновременно, а находить повторяющиеся проблемы, формулировать общий контракт, проверять его на реальном контенте и последовательно переводить систему на новое решение.