В первой части мы разобрали основы Ursina и создали простую 3D-игру. Теперь перейдем к более сложной механике — искусственному интеллекту для NPC с помощью поведенческих деревьев (Behavior Trees).
1. Что такое поведенческие деревья?
Поведенческое дерево — это структура, которая определяет логику действий NPC. В отличие от простых скриптов, оно позволяет:
-
Гибко комбинировать условия и действия
-
Легко масштабировать ИИ-логику
-
Избегать спагетти-кода в сложных сценариях
Пример простого дерева для NPC-стража:
Copy
Поведение охранника:
├─ Если видит игрока → Атаковать
├─ Если слышит шум → Исследовать
└─ Иначе → Патрулировать
2. Подключаем AI в Ursina
Ursina включает модуль Behavior для работы с поведенческими деревьями.
Шаг 1: Создаем базового NPC
python
Copy
from ursina import *
from ursina.prefabs.ai import Behavior
app = Ursina()
# NPC - это просто куб с "мозгами"
npc = Entity(model='cube', color=color.red, position=(3, 0, 0))
Шаг 2: Добавляем поведение
python
Copy
# Определяем действия NPC
def patrol():
npc.x += time.dt * 2 # Движется вправо
if npc.x > 5:
npc.x = -5 # Возвращается на старт
def chase_player():
npc.position += (player.position - npc.position).normalized() * time.dt * 3
# Создаем дерево поведения
npc.add_script(Behavior({
'sequence': [
{'condition': lambda: distance(npc, player) < 3, 'action': chase_player},
{'action': patrol}
]
}))
Как это работает:
-
NPC сначала проверяет, близко ли игрок (distance < 3).
-
Если да — преследует (chase_player).
-
Если нет — патрулирует (patrol).
3. Сложные сценарии
Пример: NPC с тремя состояниями
python
Copy
states = {
'calm': {
'action': lambda: setattr(npc, 'color', color.green),
'transition': [
{'condition': lambda: distance(npc, player) < 4, 'next_state': 'alert'},
]
},
'alert': {
'action': lambda: setattr(npc, 'color', color.yellow),
'transition': [
{'condition': lambda: distance(npc, player) < 2, 'next_state': 'angry'},
{'condition': lambda: distance(npc, player) > 5, 'next_state': 'calm'},
]
},
'angry': {
'action': chase_player,
'transition': [
{'condition': lambda: distance(npc, player) > 4, 'next_state': 'alert'},
]
}
}
npc.add_script(Behavior({'state_machine': states}))
Логика:
-
🟢 Спокойный (зеленый): игрок далеко → NPC стоит.
-
🟡 Настороженный (желтый): игрок приближается → NPC поворачивается к нему.
-
Агрессивный (красный): игрок слишком близко → погоня!
4. Продвинутые техники
4.1. Работа с памятью NPC
Чтобы NPC “запоминал” игрока даже когда тот скрылся:
python
Copy
npc.memory = {'last_seen_player_pos': None}
def remember_player():
if distance(npc, player) < 3:
npc.memory['last_seen_player_pos'] = player.position
Behavior({
'action': remember_player,
'sequence': [
{'condition': lambda: npc.memory['last_seen_player_pos'] is not None,
'action': lambda: npc.look_at(npc.memory['last_seen_player_pos'])},
{'action': patrol}
]
})
4.2. Групповое поведение
Создаем стаю NPC, которая атакует вместе:
python
Copy
enemies = [Entity(model='cube', color=color.red) for _ in range(5)]
for enemy in enemies:
enemy.add_script(Behavior({
'parallel': [
{'condition': lambda e=enemy: distance(e, player) < 4, 'action': chase_player},
{'action': lambda e=enemy: e.look_at_2d(player)}
]
}))
5. Оптимизация производительности
-
Используйте distance_squared вместо distance для проверок (избегаем квадратного корня).
-
Ограничивайте частоту проверок через time.dt:
python
Copy
def update(): if time.time() % 1.0 < time.dt: # Проверяем раз в секунду npc.bt.update()
6. Что дальше?
-
Добавьте путьfinding через ursina.pathfinding для обхода препятствий.
-
Создайте диалоговую систему — NPC могут реагировать на слова игрока.
-
Экспериментируйте с нейросетями (подключите TensorFlow для обучения NPC).
Совет: Для сложных проектов используйте визуальный редактор поведенческих деревьев Behavior Tree Editor.
Итог: С Ursina даже сложный ИИ становится доступным. Начните с простых патрулей, а затем создавайте NPC с характером!
P.S. Попробуйте сделать NPC, который:
-
Прячется за укрытиями
-
Подбирает предметы
-
Общается с другими NPC
Какой вариант реализуете первым?
Автор: TitledCube