Что такое боевая система
attack() — это архитектура из взаимосвязанных модулей, каждый из которых отвечает за свою часть логики.набор действий (атака, защита, способности, движение)
формулы, вероятности, баланс
анимации, эффекты, AI-ответ, смена состояния мира
БС = алгоритм принятия решений + расчёт исхода конфликта.
Это универсальная формула работает в Doom, Dark Souls, Pokemon, Total War — и в реальных автономных системах: дронах, военных роботах, системах противодействия БПЛА.
Что такое боевая система: глубокий взгляд
Начинающие разработчики думают о БС как о «кнопке атаки». Опытные — как о системе взаимодействия состояний и событий.
Три уровня понимания БС
Уровень 1 — поверхностный (новичок): «Нажал атаку — враг получил урон». Кнопка → функция → результат.
Уровень 2 — системный (джуниор/мидл): Атака — это событие. Событие проходит через цепочку обработчиков: проверка дистанции → расчёт попадания → расчёт урона → применение эффектов → проверка смерти → обновление состояния мира.
Уровень 3 — архитектурный (сеньор): БС — это набор независимых систем, общающихся через события и данные. Каждая система делает одно: DamageSystem считает урон, StatusEffectSystem управляет эффектами, DeathSystem обрабатывает смерть. Ни одна система не знает о другой — только об общих данных.
Логика конфликта
Любой бой — это последовательность решений:
textВвод → Проверка условий → Выбор действия
→ Расчёт результата → Применение → Обновление состояния
→ Ввод следующей итерации
В real-time системах эта петля выполняется 30-60 раз в секунду (по количеству кадров). В пошаговых — один раз на ход. Именно скорость петли и определяет тип боевой системы.
Архитектура боевой системы
Профессиональная БС строится из четырёх фундаментальных блоков.
1. Entity Component System (ECS)
Entity-Component System diagram
Современный стандарт в gamedev и реальных системах управления роботами. Идея: не наследование объектов, а композиция данных.
Вместо класса Warrior extends Character extends GameObject — три раздельных понятия:
- Entity — просто уникальный ID (число). Ничего больше.
- Component — только данные, без логики.
HealthComponent { hp: 100, maxHp: 100 },DamageComponent { attack: 15, critChance: 0.2 }. - System — только логика, без данных.
CombatSystemберёт все сущности сDamageComponent + HealthComponentи обрабатывает урон.
Почему это важно для БС:
cpp// Старый подход (ООП): плохо масштабируется
class Warrior : public Character {
void attack(Character* target) { ... }
};
// ECS подход: масштабируется идеально
struct DamageComponent { float attack; float critChance; };
struct HealthComponent { float hp; float maxHp; };
// CombatSystem обрабатывает всё, что имеет оба компонента
void CombatSystem::update(World& world) {
for (auto [attacker, target] : world.getPairs<DamageComponent, HealthComponent>()) {
float damage = calcDamage(attacker.attack, attacker.critChance);
target.hp -= damage;
}
}
ECS обеспечивает в 100 раз лучшее использование кэша процессора при большом количестве объектов. Именно поэтому Unity DOTS и Unreal Engine Niagara используют ECS под капотом.
Та же архитектура применяется для управления подводными роботами ROV: ECS позволяет переконфигурировать систему управления тягой без изменения кода — просто добавляя новые компоненты.
2. State Machine (конечный автомат)
Конечный автомат (FSM) — классический инструмент для управления поведением персонажа и врага.
Принцип: объект всегда находится в одном из N состояний. Переходы между состояниями происходят по условиям-триггерам.
pythonclass EnemyState:
IDLE = "idle" # стоит, ждёт
PATROL = "patrol" # патрулирует маршрут
CHASE = "chase" # преследует игрока
ATTACK = "attack" # атакует
DEAD = "dead" # мёртв
class EnemyFSM:
def update(self, enemy, player):
if self.state == EnemyState.PATROL:
if enemy.can_see(player, radius=15):
self.state = EnemyState.CHASE # переход
elif self.state == EnemyState.CHASE:
if enemy.distance_to(player) < 2.0:
self.state = EnemyState.ATTACK
elif not enemy.can_see(player, radius=20):
self.state = EnemyState.PATROL
elif self.state == EnemyState.ATTACK:
enemy.attack(player)
Ключевое правило ECS + FSM: каждый экземпляр врага имеет собственный State Machine. Нельзя управлять несколькими врагами одним FSM — они будут синхронно двигаться, как «отряд роботов-танцоров».
Ограничение FSM: при усложнении логики граф состояний взрывается. Для 10 состояний может потребоваться 45 переходов. Именно поэтому для сложного AI применяют Behavior Tree.
3. Behavior Tree (дерево поведения)
Behavior Tree (BT) — эволюция FSM для сложного AI. Вместо плоского графа состояний — иерархическое дерево решений.
Структура BT:
- Sequence (
→): выполняет дочерние узлы по порядку, пока один не провалится - Selector (
?): перебирает дочерние узлы, пока один не успешен - Leaf (листовой узел): конкретное действие (
Attack,MoveTo,Reload)
textRoot
├─ Selector (выбрать действие)
│ ├─ Sequence (атаковать если близко)
│ │ ├─ IsPlayerInRange(2.0) # условие
│ │ └─ AttackPlayer() # действие
│ ├─ Sequence (преследовать если видит)
│ │ ├─ CanSeePlayer()
│ │ └─ ChasePlayer()
│ └─ Patrol() # дефолт
BT vs FSM: FSM лучше для простых объектов с 3-5 состояниями. BT — для сложного AI с десятками условий. Unreal Engine 5 перешёл от Behavior Tree к State Tree — гибриду, объединяющему преимущества обоих подходов.
4. Event System (система событий)
Компоненты БС не должны знать друг о друге — они общаются через события. Это делает систему модульной и тестируемой.
csharp// Событие — простая структура данных
public struct DamageEvent {
public int AttackerId;
public int TargetId;
public float Amount;
public DamageType Type; // Physical, Fire, Magic
}
// CombatSystem публикует событие
eventBus.Publish(new DamageEvent {
AttackerId = warrior.Id,
TargetId = goblin.Id,
Amount = 42.5f,
Type = DamageType.Physical
});
// HealthSystem подписан и обрабатывает
eventBus.Subscribe<DamageEvent>(ev => {
var target = world.GetComponent<HealthComponent>(ev.TargetId);
target.hp -= ev.Amount;
if (target.hp <= 0) eventBus.Publish(new DeathEvent(ev.TargetId));
});
// AnimationSystem тоже подписан — независимо воспроизводит хит-анимацию
// UISystem тоже подписан — обновляет полоску HP
Главное правило: анимации не должны быть привязаны к логике. Логика публикует события, визуал их потребляет — они не знают друг о друге.
Математика боя
Базовая формула урона
Любая формула урона решает одну задачу: как соотнести атаку атакующего с защитой цели.
Формула А — линейная (самая простая):
Damage=Attack−Defense
Проблема: при Defense ≥ Attack урон = 0. Стена урона — плохой геймдизайн.
Формула B — мультипликативная (D&D-стиль):
Damage=BaseAttack×(Defense0.85Strength0.85)
Здесь показатель 0.85 (вместо 1.0) смягчает кривую: очень высокая защита уменьшает урон, но не обнуляет. Из реального решения на gamedev.stackexchange.
Формула C — процентная (WoW/MMO-стиль):
DamageReduction=Defense+kDefense
где k — константа баланса (например, 100). При Defense = 100, k = 100: редукция = 50%. Никогда не достигает 100% — защита асимптотична.
Формула D — полная (с крит-ударом и вариативностью):
FinalDamage=BaseDmg×DefMod×CritMod×Random(0.85,1.15)
pythonimport random
def calculate_damage(attacker, target):
# Базовый урон
base_dmg = attacker.attack
# Модификатор защиты (формула C)
k = 100
def_mod = 1.0 - (target.defense / (target.defense + k))
# Критический удар
is_crit = random.random() < attacker.crit_chance
crit_mod = attacker.crit_multiplier if is_crit else 1.0
# Случайный разброс ±15%
variance = random.uniform(0.85, 1.15)
final = base_dmg * def_mod * crit_mod * variance
return round(final), is_crit
Вероятность попадания
В системах с промахами (D&D, XCOM, Fire Emblem) шанс попадания — отдельная формула:
P(hit)=Accuracy−Evasion+Modifiers
XCOM сделал знаменитый выбор: «two-roll system» для снижения субъективного ощущения несправедливости при промахе 95%: система делает два броска и берёт лучший, сдвигая реальный шанс ближе к экстремумам.
Баланс и масштабирование
Проблема любой RPG: как масштабировать урон при росте уровней, чтобы бой оставался интересным?
AI в боевой системе
Стек технологий для игрового AI
textУровень 4: Стратегический AI → планирование боя (GOAP, HTN)
Уровень 3: Тактический AI → выбор цели, позиции (Behavior Tree)
Уровень 2: Управление состоянием → что делать сейчас (State Machine / State Tree)
Уровень 1: Перемещение → навигационная сетка (NavMesh, A*)
GOAP: Goal-Oriented Action Planning
GOAP — продвинутая архитектура AI, где агент сам планирует последовательность действий для достижения цели. Использовался в F.E.A.R. (2005) и остаётся актуальным сегодня.
pythonclass GOAPAgent:
def __init__(self):
self.goal = {"enemy_dead": True}
self.state = {"has_weapon": True, "enemy_visible": False, "in_cover": False}
self.actions = [
Action("seek_cover", pre={"enemy_visible": True}, eff={"in_cover": True}),
Action("find_enemy", pre={"enemy_visible": False}, eff={"enemy_visible": True}),
Action("attack", pre={"has_weapon": True, "enemy_visible": True},
eff={"enemy_dead": True}),
]
def plan(self):
# A* по графу состояний — AI сам строит план
return astar_plan(self.state, self.goal, self.actions)
# Результат: [find_enemy → attack] или [seek_cover → find_enemy → attack]
Модульный Combat AI на Unreal Engine 5
Профессиональный пример из реального проекта: пять врагов, использующих:
- Восприятие (
AISenseConfig_Sight,Hearing): видят и слышат игрока - Координацию: группа выбирает лидера и фланкеров
- Адаптацию: меняют тактику при потере члена группы
cpp// Unreal Engine 5 C++ — базовый Combat AI
void ACombatAI::UpdateBehavior() {
if (PerceptionComp->GetCurrentlyPerceivedActors().Num() > 0) {
// Переход в боевой режим через BlackBoard
BlackboardComp->SetValueAsEnum("AIState", (uint8)EAIState::Combat);
// Выбор тактики в зависимости от расстояния
float dist = GetDistanceTo(CurrentTarget);
if (dist < MeleeRange) {
BrainComp->StartLogic(); // Behavior Tree: ближний бой
} else {
BlackboardComp->SetValueAsVector("CoverPoint", FindBestCover());
}
}
}
Практика программирования: от нуля до рабочей системы
Минимальная БС за 50 строк (Python)
pythonimport random
from dataclasses import dataclass, field
from typing import List
@dataclass
class Fighter:
name: str
hp: int
max_hp: int
attack: int
defense: int
speed: int
alive: bool = True
effects: List[str] = field(default_factory=list)
def is_alive(self):
self.alive = self.hp > 0
return self.alive
def calc_damage(atk: Fighter, target: Fighter) -> tuple[int, bool]:
k = 50 # константа баланса
def_mod = 1.0 - (target.defense / (target.defense + k))
is_crit = random.random() < 0.15 # 15% крит
crit_mod = 1.75 if is_crit else 1.0
raw = atk.attack * def_mod * crit_mod * random.uniform(0.9, 1.1)
return max(1, int(raw)), is_crit # минимум 1 урон
def combat_round(a: Fighter, b: Fighter):
# Определяем очерёдность по speed
order = sorted([a, b], key=lambda f: f.speed, reverse=True)
for attacker in order:
defender = b if attacker is a else a
if not attacker.is_alive(): continue
dmg, crit = calc_damage(attacker, defender)
defender.hp -= dmg
tag = " 💥КРИТ!" if crit else ""
print(f" {attacker.name} → {defender.name}: -{dmg}{tag} (HP: {max(0,defender.hp)}/{defender.max_hp})")
def run_combat(player: Fighter, enemy: Fighter):
print(f"\n⚔️ {player.name} vs {enemy.name}\n{'─'*40}")
round_num = 1
while player.is_alive() and enemy.is_alive():
print(f"\nРаунд {round_num}:")
combat_round(player, enemy)
round_num += 1
winner = player if player.is_alive() else enemy
print(f"\n🏆 Победитель: {winner.name}")
# Запуск
hero = Fighter("Герой", hp=120, max_hp=120, attack=25, defense=15, speed=10)
goblin= Fighter("Гоблин", hp=80, max_hp=80, attack=18, defense=8, speed=12)
run_combat(hero, goblin)
C# (Unity): компонентная БС
csharp// HealthComponent.cs
public class HealthComponent : MonoBehaviour {
[SerializeField] float maxHp = 100f;
float currentHp;
public event System.Action<float, float> OnHealthChanged;
public event System.Action OnDeath;
void Awake() => currentHp = maxHp;
public void TakeDamage(float amount) {
currentHp = Mathf.Max(0, currentHp - amount);
OnHealthChanged?.Invoke(currentHp, maxHp);
if (currentHp <= 0) OnDeath?.Invoke();
}
}
// CombatSystem.cs — обработчик атак
public class CombatSystem : MonoBehaviour {
[SerializeField] float attackPower = 25f;
[SerializeField] float critChance = 0.15f;
public void Attack(GameObject target) {
var health = target.GetComponent<HealthComponent>();
if (health == null) return;
float damage = attackPower;
bool isCrit = Random.value < critChance;
if (isCrit) damage *= 1.75f;
health.TakeDamage(damage);
Debug.Log($"Hit for {damage:F0}{(isCrit ? " CRIT!" : "")}");
}
}
Выбор игрового движка и языка
| Движок | Язык | Лучше для | Особенности БС |
|---|---|---|---|
| Unity | C# | мобайл, инди, прототипы | DOTS/ECS для оптимизации, NavMesh встроен |
| Unreal Engine 5 | C++ + Blueprint | AAA, сложный AI, FPS/TPS | Behavior Tree, State Tree, Lyra Combat Framework |
| Godot 4 | GDScript / C# | открытый исходник, 2D/3D | Прост в освоении, хорош для инди |
| Pygame / Python | Python | прототипы, обучение | Быстрый старт, не для продакшена |
| SDL2 / SFML | C++ | кастомный движок | Полный контроль, высокая производительность |
Типы боевых систем
Real-Time Combat (RTС)
Действие происходит непрерывно, решения принимаются мгновенно. Петля обновления выполняется на каждом кадре (60 fps = 60 итераций/сек).
Архитектурные особенности:
- Collision Detection — обнаружение пересечений хитбоксов в реальном времени
- Input Buffer — буфер ввода для регистрации комбо (12-16 кадров в файтингах)
- Interpolation — плавное движение снарядов между кадрами (lerp, bezier)
- Rollback Netcode — для мультиплеера: откат и воспроизведение кадров при рассинхронизации
Примеры: Dark Souls, Devil May Cry, StarCraft II, Halo.
Turn-Based Combat (TBS)
Игроки и враги ходят по очереди. Петля боя — одна итерация на ход.
Архитектурные особенности:
- Command Queue — очередь действий; каждое действие — объект класса
Command - Turn Order System — порядок ходов (по Speed, Initiative, FIFO)
- Undo System — возможность отмены действий
- Prediction System — показ предполагаемого урона до подтверждения
csharp// Command Pattern для пошаговой боевой системы
public interface IBattleCommand {
void Execute();
void Undo(); // для отмены
}
public class AttackCommand : IBattleCommand {
Fighter attacker, target;
int damage;
public AttackCommand(Fighter atk, Fighter tgt) {
attacker = atk; target = tgt;
}
public void Execute() {
damage = CombatMath.CalcDamage(attacker, target);
target.TakeDamage(damage);
}
public void Undo() => target.Heal(damage); // откат
}
// BattleManager — управляет очередью
Queue<IBattleCommand> commandQueue = new Queue<IBattleCommand>();
commandQueue.Enqueue(new AttackCommand(player, enemy));
commandQueue.Dequeue().Execute(); // выполнить следующее действие
Примеры: Pokemon, Final Fantasy VII, XCOM, Fire Emblem.
Hybrid (ATB / RTWP)
Active Time Battle (Final Fantasy IV-IX) и Real-Time with Pause (Baldur’s Gate, Dragon Age). Смесь: реальное время с возможностью паузы.
ATB-таймер — каждый персонаж имеет заполняющийся индикатор:
python# ATB: Active Time Battle
def update_atb(fighters: list, delta_time: float):
for f in fighters:
f.atb_charge += f.speed * delta_time
if f.atb_charge >= ATB_MAX:
f.atb_charge = 0
action_queue.append(f) # готов к действию
Реальные системы: от игр к дронам и роботам
Архитектура БС напрямую применяется в реальных автономных системах.
ECS в управлении роботами
Та же ECS-архитектура, используемая в Unity DOTS, применяется для управления подводными ROV. Система ECS позволяет ROV переключаться между конфигурациями тяги без изменения кода управления — достаточно изменить компоненты. Ошибка ориентации при перестройке остаётся ниже 2° по рысканью и 4° по крену.
State Machine в автономных дронах
State Machine военного дрона структурно идентичен FSM игрового врага:
| Игровой AI-враг | Автономный дрон |
|---|---|
IDLE | STANDBY (ожидание команды) |
PATROL | PATROL (маршрутный полёт) |
CHASE | TRACK (сопровождение цели) |
ATTACK | ENGAGE (выход на рубеж атаки) |
RETREAT | RTB (возврат на базу) |
Роевые алгоритмы
Управление роями дронов использует те же алгоритмы, что и управление отрядами в RTS-играх: разделение задач между агентами, выбор цели, координация действий без центрального управления. FormationComponent в игровом ECS и модуль построения боевых порядков дрона — один и тот же архитектурный паттерн.
Алгоритм A* — навигация везде
Алгоритм поиска пути A* (A-star) используется и в играх (NavMesh), и в реальных роботах (планирование маршрута), и в GOAP (планирование действий AI). Это фундаментальный алгоритм оборонных и игровых систем.
Типичные ошибки при создании БС
Дисбаланс
Проблема: один персонаж/тактика доминирует над всеми остальными.
Причины: формула урона не протестирована на крайних значениях, нет playtesting-данных, разработчик не играл в собственную игру.
Решение: матрица баланса — таблица, где каждый класс/персонаж проверяется против каждого другого. Автоматизированный симулятор боёв (10 000 итераций) покажет дисбаланс без ручного тестирования.
Плохой AI
Проблема: враги предсказуемы или атакуют стеной.
Причина: FSM без случайности, одна State Machine для всех врагов, нет разнообразия тактик.
Решение: добавить random.uniform(0, 1.5) задержку перед атакой — создаёт иллюзию «думающего» врага. Разные State Machine для разных типов врагов. BT вместо FSM при >5 состояниях.
Жёсткая связанность (Tight Coupling)
Проблема: AnimationSystem вызывает методы DamageSystem напрямую → изменение одного ломает другое.
Решение: Event Bus. Системы только публикуют и подписываются на события, никогда не вызывают друг друга напрямую.
Игнорирование latency в мультиплеере
Проблема: в сети каждый клиент видит разное состояние мира — кто-то стоит, кто-то уже мёртв.
Решение: Rollback Netcode (фактический стандарт в файтингах) — все клиенты симулируют одинаковую детерминированную игру, при рассинхронизации откатываются к точке расхождения и воспроизводят заново.
Нарушение правила: логика ≠ визуал
Симптом: анимация атаки проигрывается, но урон уже применён в кадр 0 — визуально не совпадает.
Решение: урон применяется через Animation Event в конкретный кадр анимации, а не при нажатии кнопки. Логика следует за анимацией, а не наоборот.
Decision Engine: что делать в зависимости от вашей цели
| Кто вы | Что делать прямо сейчас | Ресурсы |
|---|---|---|
| Новичок | Написать пошаговую БС на Python (100 строк). Освоить: классы, циклы, random, условия | Python + туториалы по RPG combat на YouTube |
| Джуниор-разработчик | Реализовать FSM для врага в Unity/Godot. Добавить Event Bus. Прочитать «Game Programming Patterns» (Robert Nystrom, бесплатно онлайн) | Unity Learn, Godot Docs, gamedev.net |
| Мидл/Сеньор | Перейти на ECS архитектуру (Unity DOTS или Flecs). Реализовать GOAP для AI. Написать симулятор баланса | Unity DOTS docs, Flecs C++ library, GDC talks |
| Геймдизайнер | Спроектировать таблицу баланса в Google Sheets. Описать все состояния FSM до написания кода. Сыграть 100 часов в жанр, который делаете | Game Balance Concepts (курс Ian Schreiber) |
| Инженер → AI/Robotics | Изучить ROS 2 (Robot Operating System), реализовать State Machine для движущегося агента, перейти к SLAM-навигации | ROS 2 Docs, MIT OpenCourseWare Robotics, arxiv.org |
| Продакт/стартап | Оценить рынок: Defence-Tech AI + игровые движки как платформа для симуляции. Unity и Unreal уже продаются армиям для тренировок | Defense Innovation Unit (DIU), SimX, Booz Allen |
FAQ
Как программируется боевая система?
Через декомпозицию на независимые модули: Entity Component System (данные), State Machine или Behavior Tree (логика AI), Event Bus (коммуникация между модулями), математические формулы (расчёт исхода). Каждый модуль делает одно и не знает о других.
Какой язык нужен для разработки БС?
Зависит от платформы: C# для Unity (самый популярный выбор для инди), C++ для Unreal Engine (AAA-производительность), Python для прототипирования и обучения, GDScript для Godot (открытый исходник). Фундаментальные концепции (ECS, FSM, BT) работают на любом языке.
Сложно ли программировать боевые системы?
Начать просто — пошаговую БС на Python можно написать за несколько часов. Масштабировать сложно: балансировка, мультиплеер, AI и производительность при тысячах объектов требуют глубоких знаний. Кривая обучения крутая, но предсказуемая.
С чего начать новичку?
- Написать текстовый RPG-бой на Python (без движка, только логика)
- Добавить классы, инвентарь, статусные эффекты
- Перейти на Unity/Godot, реализовать визуальную версию
- Изучить State Machine для AI-противника
- Прочитать «Game Programming Patterns» Роберта Нистрома (бесплатно: gameprogrammingpatterns.com)
Есть ли связь между игровыми БС и реальными военными системами?
Прямая. ECS-архитектура применяется для управления ROV. State Machine дронов идентичен FSM игрового врага. GOAP используется в реальных системах планирования. Алгоритм A* — стандарт и в NavMesh, и в робототехнике. Это не метафора — это буквально один и тот же код.
Что такое rollback netcode и нужен ли он?
Rollback netcode — техника синхронизации мультиплеера, где каждый клиент симулирует детерминированную игру, при рассинхронизации откатывается к точному состоянию и воспроизводит ввод заново. Необходим для онлайн-файтингов и любых игр с требованием низкой задержки. Для одиночных игр — не нужен.
Вывод: БС — это ядро любой системы принятия решений
Боевая система — это не игровая механика. Это универсальная архитектура принятия решений в условиях конфликта. Та же логика FSM, что управляет гоблином в RPG, управляет дроном на поле боя. Тот же ECS, что обрабатывает тысячи снарядов в Unreal Engine, управляет конфигурацией тяги подводного робота. Тот же алгоритм A*, что прокладывает путь NPC через NavMesh, строит маршрут автономного агента.
Рынок движется туда, где игровые технологии и реальные системы сходятся. Unity и Unreal продают лицензии армиям для военных симуляторов. GOAP и Behavior Tree из игровых движков перекочёвывают в автономные системы. Разработчик, понимающий архитектуру БС, может работать в GameDev, Defence-Tech, робототехнике и AI — это один и тот же фундамент.
Ваш следующий шаг:
- Если вы новичок — откройте Python и напишите первые 50 строк боя сегодня
- Если вы мидл — реализуйте Behavior Tree и сравните с вашим текущим FSM
- Если вы сеньор — изучите, как ваша архитектура масштабируется до 10 000 объектов через ECS
Нужна консультация по архитектуре боевой системы для вашего проекта? Или помощь с переходом из GameDev в Defence-Tech AI? Оставьте заявку — разберём вашу задачу конкретно.








