Програмування системи штучного інтелекту ворогів
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 та підтримує runtime-оновлення для динамічних перешкод через 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 та координація — для тактичних 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: на відстані вороги переключаються на спрощену поведінку без пересчета шляху.





