← Puzzle Breakers

ТЕХНИЧЕСКИЙ КЕЙС · Архитектура

Эволюция архитектуры боевой системы

Как развивалась архитектура LiveOps-игры: от разрозненных реализаций к общим контрактам и расширяемым механизмам.

UnityC#LiveOpsИгровая архитектураРефакторинг

Контекст

Архитектура боевой системы развивалась вместе с игрой и регулярным выпуском контента. Новые герои, способности, предметы и боевые сущности добавлялись годами, поэтому удачное решение для одной механики со временем могло стать источником дублирования, неявных зависимостей и расхождения поведения.

Моя задача состояла не в разовой переработке, а в последовательной модернизации существующих решений: предлагать общие абстракции там, где они снижали стоимость развития, и проводить миграции небольшими шагами, не останавливая производство контента.

Архитектурные направления

Единая точка изменений состояния боя

Операции, меняющие состояние боя, исторически были распределены по нескольким базовым классам и защищённым методам. Это создавало дублирование, различия в поведении и мешало повторному использованию логики новыми боевыми сущностями.

Я вёл поэтапную консолидацию таких операций в единый интерфейс IBattleActions: переносил работу с уроном, лечением, маной, энергией, статусами, характеристиками, спавном и действиями акторов. Изменения выполнялись по доменам небольшими итерациями, поэтому текущие задачи по контенту могли продолжаться параллельно с архитектурной миграцией.

До:     базовые классы и локальные protected-методы
              │
              ├── разное поведение в похожих сценариях
              └── копирование логики новыми сущностями

После:  скрипт сущности → IBattleActions → единые правила изменения состояния боя

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

Общая модель боевых сущностей

Акторы, фишки, обвесы фишек, расходуемые предметы, боевые модификаторы и триггеры имели похожую задачу: нести кастомную игровую логику. Ранее жизненный цикл и создание скриптов для этих типов развивались по отдельности.

Я участвовал в унификации этой модели вокруг базового класса BattleEntityScript, интерфейсов сущностей и фабрик с единым контрактом. Это сделало добавление новой сущности более предсказуемым: вместо создания отдельного жизненного цикла нужно было встроиться в уже известную архитектурную схему.

Выразимые параметры вместо булевых цепочек

Расчёт урона и лечения — одна из самых часто вызываемых частей боевой системы. Когда методы принимают ряд позиционных булевых параметров, их легко перепутать или неверно интерпретировать, а компилятор не всегда может обнаружить ошибку.

Я перевёл эти вызовы на параметр-объекты DamageOptions и HealOptions с именованными свойствами и переиспользуемыми значениями по умолчанию. Это сделало намерение вызова понятнее, снизило риск ошибок при изменении сигнатур и позволило безопаснее развивать общие правила расчёта.

// Условный пример принципа, не код проекта
// Было: порядок флагов легко перепутать.
ApplyDamage(source, target, value, true, false, true);

// Стало: назначение каждого параметра видно в месте вызова.
ApplyDamage(source, target, value, new DamageOptions
{
    IsAbility = true,
    IsRaw = false
});

Результат

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

Главный принцип этой работы — не заменять всё одновременно, а находить повторяющиеся проблемы, формулировать общий контракт, проверять его на реальном контенте и последовательно переводить систему на новое решение.