За пределами enum State: Создание масштабируемого, настраиваемого ИИ для врагов в Unity на основе Scriptable Objects.
Разработка поведенческого ИИ для неигровых персонажей (NPC) в Unity часто начинается с простого и понятного шаблона: конечный автомат (FSM), реализованный через switch или if-else операторы над enum-перечислением состояний. Этот подход быстро становится препятствием по мере роста сложности проекта. Монолитный скрипт EnemyAI на тысячи строк, в котором перемешана логика патрулирования, атаки, реакция на звуки и обработка дебаффов, превращается в кошмар поддержки. Добавление нового поведения (например, эффекта "испуга") требует внесения хаотичных правок в десятки мест, нарушая принцип единственной ответственности (Single Responsibility Principle) и делая код хрупким.
В этой статье мы представим архитектурное решение, которое трансформирует разработку ИИ из задачи программирования в задачу конфигурации и композиции. Мы откажемся от управления состоянием через enum в пользу независимых, переиспользуемых ScriptableObject-ассетов для каждого поведения. Вы увидите, как:
Декомпозировать монолит на изолированные компоненты: PatrolStateSO, ChaseStateSO, AttackStateSO, EffectStateSO.
Интегрировать навигацию (NavMesh) и систему обнаружения игрока, не создавая жестких зависимостей.
Реализовать систему эффектов (оглушение, замедление), которая может как временно перехватывать контроль, так и модифицировать активное состояние.
Предоставить геймдизайнерам мощный и безопасный инструмент для настройки поведения врагов прямо в инспекторе Unity, без необходимости писать код.
Эта архитектура не просто решает проблему "спагетти-кода" — она создает экосистему для создания сложного, разнообразного и легко отлаживаемого ИИ, где новый тип поведения или эффект добавляется за минуты, а не за часы.
Часть 1: Архитектурный фундамент — State Machine на Scriptable Objects
1.1 Почему мы отказались от классического FSM в коде
Традиционная реализация конечного автомата в Unity часто выглядит так:
Проблемы такого подхода:
- Нарушение SRP (Single Responsibility Principle) — один класс отвечает за всё
- Сложность расширения — добавление нового состояния требует модификации существующего кода
- Плохая настраиваемость — параметры поведения захардкожены в скриптах
- Сложность отладки — логика всех состояний перемешана в одном месте
1.2 ScriptableObjects как идеальная абстракция для состояний
Мы выбрали ScriptableObjects по нескольким ключевым причинам:
Преимущества SO для FSM:
- Сепарация данных и логики
- Горячая перезагрузка (Hot Reload)
- Изменения параметров в инспекторе применяются моментально
- Не требует перекомпиляции или перезапуска игры
- Идеально для итеративного дизайна баланса
- Переиспользование и композиция
- Один PatrolStateSO может использоваться разными типами врагов. Параметры можно настраивать для каждого врага индивидуально
- Возможность создавать пресеты поведения
- Визуальное управление в инспекторе
1.3 Базовый класс AIStateSO: архитектура жизненного цикла
Рассмотрим ядро нашей системы — абстрактный класс AIStateSO:
Ключевые особенности реализации:
- Шаблонный метод (Template Method Pattern)
1.1Базовый класс определяет структуру (OnEnter → OnUpdate → OnExit) - Производные классы переопределяют конкретные шаги
- Инверсия зависимостей (Dependency Inversion)
- Поддержка асинхронных операций
1.4 Контекст AIEnemy: управление без микроменеджмента
Класс AIEnemy перестает быть "мозгом" врага и становится "диспетчером":
Важные архитектурные решения:
- Разделение шаблонов и экземпляров
- Паттерн "Состояние" (State Pattern) в чистом виде
- Контекст (AIEnemy) делегирует работу объекту состояния
- Состояния могут заменять друг друга через контекст
1.5 Практический пример: создание нового состояния
Демонстрация расширяемости системы — добавляем FleeStateSO (состояние бегства):
Результат: Мы добавили сложное поведение (тактическое отступление) без:
- Изменения существующего кода
- Модификации класса AIEnemy
- Риска сломать другие состояния
1.7 Выводы по архитектуре
Что мы получили:
- Чистая архитектура — каждый компонент решает одну задачу
- Невероятная гибкость — новые поведения добавляются за минуты
- Дизайнер-фриендли — настройка без программиста
- Легкость отладки — состояния изолированы, ошибки локализуются
Ключевые инсайты:
- ScriptableObjects — это не только для данных, но и для поведения
- Состояния должны быть глупыми — они получают все необходимое через параметры
- Контекст должен быть минимальным — его задача только переключать состояния
- Копирование SO обязательно — иначе состояние разделяется между врагами
Часть 3: Интеграция NavMesh — От хаотичного управления к системному подходу
В AIEnemyOLD.cs работа с NavMesh была распределена по всему коду без единой стратегии:
"Умный" код с побочными эффектами:
Принципы проектирования навигации в новой системе
Интеграция навигации с состояниями:
Завершение
В рамках данной статьи мы представили модульную, расширяемую архитектуру искусственного интеллекта для врагов в Unity, построенную на основе конечного автомата с использованием ScriptableObjects. Эта система представляет собой не просто реализацию FSM, а продуманный фундамент для создания сложных поведенческих систем любого масштаба.