Проектирование боевых систем и механик взаимодействия
Боевая система ломается не там, где её проектируют — она ломается в стыке между анимацией, хитбоксом и логикой нанесения урона. Типичная картина: дизайнер расставил окно атаки в Animation Event на кадре 12, программист читает его в OnAttackHit(), а QA находит, что удар проходит сквозь врага при fps ниже 30 — потому что Physics.OverlapSphere вызывается ровно один раз в момент события, и коллайдер врага между кадрами не перекрывается с хитбоксом.
Архитектура хитбокс-системы
Профессиональная реализация разделяет hurtbox (область, по которой можно попасть) и hitbox (область, которой персонаж атакует). Обе — отдельные коллайдеры на дочерних объектах, управляемые через компоненты Hurtbox : MonoBehaviour и Hitbox : MonoBehaviour.
Hitbox активируется не через SetActive(), а через переключение isTrigger и слоя — это дешевле по производительности при частых вкл/выкл. За одну атаку hitbox должен попасть в каждый hurtbox только один раз: это контролируется через HashSet<int> с InstanceID уже поражённых целей, который очищается при деактивации hitbox.
Для оружия ближнего боя с быстрыми движениями OverlapSphere за один кадр недостаточно. Надёжнее Physics.CapsuleCast от позиции оружия на предыдущем кадре до текущей — это ловит цели, оказавшиеся в траектории движения клинка между кадрами. previousPosition хранится в LateUpdate() предыдущего кадра.
Управление состояниями боя
Конечный автомат боевых состояний — это не Animator State Machine. Animator управляет визуалом; игровая логика живёт в отдельном CombatStateMachine. Состояния: Idle, Attacking, Recovering, Staggered, Blocking, Parrying. Каждое состояние — отдельный класс с Enter(), Update(), Exit().
Приоритет отмены атак (cancel priority) — одна из самых сложных частей. В файтингах и action-RPG игрок должен иметь возможность отменить часть анимации атаки в дэш или следующий удар. Это реализуется через cancelWindows[] — массив структур с startFrame, endFrame, allowedCancels. Когда нормализованное время Animator попадает в окно, флаг canCancel поднимается, и CombatStateMachine принимает новый ввод.
Системы взаимодействия: интерактивные объекты и диалоги
Компонент взаимодействия строится по схеме Interactable / Interactor. IInteractable — интерфейс с методом Interact(GameObject interactor). InteractorComponent на игроке держит List<IInteractable> в радиусе действия, обновляемый через OnTriggerEnter/Exit. При нажатии кнопки вызывается closest.Interact(gameObject).
Частая ошибка — реализовать взаимодействие через Raycast в Update() каждый кадр. Это и лишние расчёты, и проблемы с приоритетом при нескольких объектах в луче. Trigger-зона с OverlapSphere раз в 0.1 секунды через InvokeRepeating дешевле и надёжнее.
Для диалоговых взаимодействий важна очередь событий. Если во время диалога игрок ещё раз нажимает кнопку, следующий Interact не должен обрабатываться до закрытия текущего. Флаг isInteracting в InteractorComponent блокирует новые взаимодействия — и снимается через событие OnInteractionComplete.
Баланс отклика и читаемости
Feedback на попадание критичен для feel боёвки. Минимальный набор: hitpause (остановка анимации атакующего на 2–4 кадра при попадании), screen shake через CinemachineImpulse, звуковой эффект с вариацией питча. Hitpause реализуется через временное выставление animator.speed = 0 и Time.timeScale не трогается — это важно, если есть UI или другие системы.
Damage numbers — отдельный разговор. Floating text с TextMeshPro должен инстанцироваться из пула, а не через Instantiate() каждый удар. При активной боёвке с AoE атаками без пула легко получить 50+ инстанцирований в секунду и GC spike.
Ориентиры по срокам
| Масштаб | Состав | Срок |
|---|---|---|
| Базовый | Одна атака, hurtbox/hitbox, HP-компонент | 3–6 дней |
| Средний | Combo система, блок, парирование, i-frames | 2–3 недели |
| Расширенный | Несколько видов оружия, способности, статус-эффекты | 4–6 недель |
| Полная система | Сетевая синхронизация боя, rollback netcode | 2–4 месяца |
Процесс проектирования
Начинаем с таблицы состояний и переходов в документе — прежде чем писать код. Каждое состояние, каждый переход, условие и приоритет. Это вскрывает конфликты ещё на этапе дизайна: например, что происходит, если игрок получает урон в момент парирования — stagger или нет?
Затем — прототип с placeholder-анимациями (даже кубами) для проверки тайминга окон атаки и cancel-системы. Интеграция с реальными анимациями — последний этап, не первый.





