Программирование системы искусственного интеллекта (AI) врагов
NavMeshAgent.SetDestination(player.position) в Update() каждый кадр — это не AI, это управляемая кукла. Враг, который всегда знает где игрок, всегда идёт к нему напрямую и атакует как только дошёл, не создаёт ни интересного геймплея, ни ощущения интеллекта. Реальная AI-система строится вокруг восприятия, принятия решений и памяти — а не просто навигации.
Perception: как враг воспринимает мир
Первый слой AI — сенсоры. Враг не должен «знать» о игроке через прямую ссылку в Update(). Восприятие моделируется:
Field of View — конусная зона проверки. Не Physics.OverlapSphere сам по себе: сначала расстояние (Vector3.Distance < detectionRange), потом угол (Vector3.Angle(forward, dirToPlayer) < fovAngle / 2), потом Physics.Linecast для проверки линии видимости без препятствий. Без последнего враги видят сквозь стены.
Hearing — радиус без проверки угла, но с фильтром по типу звука. Шаги на металле слышны на 15 единиц, на мягком полу — на 5. Реализуется через AIPerceptionEvent публикуемый NoiseEmitter компонентами на движущихся объектах.
Last Known Position — критически важная концепция. Когда игрок исчезает из поля зрения, враг не «забывает» мгновенно. Хранится Vector3 lastKnownPosition и float lastSeenTime. В состоянии Investigation враг идёт к LKP, осматривается, только после таймаута переходит в Patrol.
Behaviour Tree vs State Machine: что выбрать
FSM (Finite State Machine) — для простых врагов с 3–5 состояниями. Реализуется как enum EnemyState + switch в Update() или паттерн State с классами. Быстро пишется, легко дебажится. Проблема: при добавлении новых состояний количество переходов растёт квадратично и FSM превращается в «спагетти» уже при 8–10 состояниях.
Behaviour Tree (BT) — для сложных врагов. Дерево из Selector, Sequence и Leaf нод. Selector выполняет детей слева направо, останавливается на первом успешном. Sequence выполняет всех детей, останавливается на первом неуспешном. Leaf-ноды — атомарные действия (MoveToTarget, AttackPlayer, PlayAnimation) и условия (IsPlayerVisible, IsHealthLow).
Пример дерева для патрульного врага:
Selector
├── Sequence (атака)
│ ├── IsPlayerInAttackRange
│ └── AttackAction
├── Sequence (преследование)
│ ├── IsPlayerVisible
│ └── MoveToPlayerAction
├── Sequence (расследование)
│ ├── HasLastKnownPosition
│ └── MoveToLKPAction
└── PatrolAction
Популярные реализации BT для Unity: NodeCanvas, Behavior Designer, Unity Muse Behavior (официальный пакет 2023+). Кастомная реализация оправдана только для специфических нужд — готовые инструменты экономят недели.
NavMesh: навигация и типичные проблемы
NavMeshAgent — компонент Unity для автоматической навигации по запечённому NavMesh. Базовое использование: agent.SetDestination(target). Но есть нюансы.
NavMeshAgent застревает на стыке двух NavMeshSurface — классическая проблема при аддитивной загрузке сцен. Каждая сцена имеет свою поверхность, соединения через NavMeshLink нужно настраивать явно. В Unity 2022+ компонент NavMeshSurface из пакета AI Navigation заменяет старый baked NavMesh и поддерживает рантайм-обновление для динамических препятствий через NavMeshObstacle.
SetDestination каждый кадр — лишняя нагрузка. Пересчёт пути занимает несколько миллисекунд. Рекомендуется обновлять destination не чаще чем раз в 0.1–0.2 секунды через InvokeRepeating или проверку дистанции изменения позиции цели: если цель сдвинулась менее чем на 0.5f — пересчёт не нужен.
Stopping distance и arrive behavior. agent.stoppingDistance определяет дистанцию до цели, на которой агент останавливается. Для атакующего врага это attackRange - 0.5f. При изменении состояния (от Patrol к Chase) нужно менять stoppingDistance и speed — разные состояния требуют разных параметров агента.
Память AI и групповое поведение
Простая память врага: AIMemory компонент с List<MemoryEntry> где каждый entry хранит position, type (player, sound, corpse), time. Старые записи удаляются по таймауту. При принятии решений BT или FSM запрашивают память через GetMostRecentEntry(MemoryType.Player).
Алертинг группы — когда один враг обнаруживает игрока, он должен оповестить соседей. Реализуется через Physics.OverlapSphere на радиус alertRadius с фильтром по тегу Enemy, вызов enemy.GetComponent<AIPerception>().ReceiveAlert(lastKnownPosition). Это не требует менеджера, работает децентрализованно.
Flanking и coordination — для тактических AI. Одна из техник: NavMeshAgent.SamplePathPosition() используется для нахождения позиций на фланге игрока, враги распределяются по этим позициям через менеджер группы. Детали реализации зависят от жанра.
Ориентировочные сроки
| Сложность AI | Состав | Срок |
|---|---|---|
| Простой FSM | Patrol, Chase, Attack, 3 состояния | 3–5 дней |
| Средний | Perception, LKP, Investigation, Flee | 1–2 недели |
| Полный BT | NodeCanvas/Behavior Designer, группы, координация | 3–6 недель |
| Сложный тактический | Flanking, cover system, squad AI | 2–3 месяца |
Процесс и отладка
AI отлаживается через Gizmos: нарисовать FOV-конус, LKP-точку, текущий путь агента, активное состояние над головой врага. Без визуализации в редакторе разобраться в поведении AI в рантайме практически невозможно.
Профилирование: NavMesh.pathfindingTimeSlice (время на путь за кадр), количество активных агентов. На мобильных платформах более 20 активных NavMeshAgent одновременно начинают заметно нагружать CPU. Решение — LOD для AI: на расстоянии враги переключаются на упрощённое поведение без пересчёта пути.





