В индустрии видеоигр искусственный интеллект (Artificial Intelligence, AI) обычно называют процесс принятия решений не управляемыми игроком персонажами. Он может быть простым: враг видит игрока и атакует. Или же более сложными, например, управляемый ИИ противник в стратегии реального времени.
В Unreal Engine создавать ИИ можно с помощьюдеревьев поведения. Дерево поведения (behavior tree) — это система определенияповедения, используемого ИИ. Например, у него может быть поведение боя или бега. Можно создать дерево поведения, при котором ИИ будет драться с игроком, если его здоровье выше. Если оно ниже 50%, то он будет убегать.
В этом туториале вы научитесь следующему:
- Создавать ИИ-сущность, которая может управлять элементом Pawn
- Создавать и использовать деревья поведения и blackboard
- Использовать AI Perception, чтобы дать Pawn зрение
- Создавать поведения, чтобы Pawn мог ходить и атаковать врагов
Примечание:Эта статья является одной из девяти частей серии туториалов по Unreal Engine:
- Часть 1: Знакомство с движком
- Часть 2: Blueprints
- Часть 3: Материалы
- Часть 4: UI
- Часть 5: Как создать простую игру
- Часть 6: Анимация
- Часть 7: Звук
- Часть 8: Системы частиц
- Часть 9: Искусственный интеллект
- Часть 10: Как создать простой FPS
Приступаем к работе. Искусственный интеллект
Скачайтезаготовку проектаи распакуйте её. Перейдите в папку проекта и откройтеMuffinWar.uproject.
Нажмите наPlay, чтобы запустить игру.Нажимайте левой клавишей мышивнутри огороженной области для создания маффина.
В этой части туториала мы создадим AI (Искусственный интеллект), который будет бродить по экрану. Когда вражеский маффин попадает в поле зрения ИИ, он подходит к врагу и атакует его.
Для создания ИИ-персонажа, нам нужно три вещи:
- Тело:физическая форма персонажа. В нашем случае телом является маффин.
- Душа:сущность, управляющая персонажем. Это может быть игрок или ИИ.
- Мозг:то, как ИИ принимает решения. Мозг можно создавать разными способами, например, с помощью кода на C++, Blueprints или деревьев поведения.
У нас уже есть тело, так что нужны ещё душа и мозг. Сначала мы создадимконтроллер, который будет являться «душой».
Что такое «контроллер» (Controller)?
Контроллер — это нефизический актор, который можетвселятьсяв Pawn. Вселение позволяет контроллеру (как можно догадаться)управлятьPawn. Но что в этом контексте означает «управлять»?
Для игрока это означает, что при нажатии клавиши Pawn будет что-то делать. Контроллер получает ввод игрока и отправляет введённые данные Pawn. Также контроллер может сам обрабатывать ввод и приказывать Pawn выполнить действие.
В случае ИИ Pawn может получать информацию от контроллера или мозга (в зависимости от того, как вы это реализуете).
Для управления маффинами с помощью ИИ (искусственный интеллект) нужно создать особый тип контроллера, называемыйконтроллером ИИ (AI controller).
Создание AI Controller
Перейдите кCharacters\Muffin\AIи создайте новыйBlueprint Class. Выберите в качестве родительского классаAIControllerи назовите егоAIC_Muffin.
Затем нужно сказать маффину, чтобы он использовал новый контроллер ИИ. Перейдите вCharacters\Muffin\Blueprintsи откройтеBP_Muffin.
По умолчанию в панели Details должны отображаться параметры Blueprint по умолчанию. Если это не так, то нажмите наClass Defaultsв Toolbar.
Перейдите в панель Details и найдите разделPawn. Выберите дляAI Controller ClassзначениеAIC_Muffin. Благодаря этому будет создаваться экземпляр контроллера при создании маффина.
Поскольку мы создаём маффины динамически, нам также выбрать дляAuto Possess AIзначениеSpawned. ТакAIC_Muffinбудет автоматически вселяться вBP_Muffinпри создании.
Нажмите наCompileи закройтеBP_Muffin.
Теперь мы создадим логику, которая будет управлять поведением маффина. Для этого можно использоватьдеревья поведения.
Создание Behavior Tree. Искусственный интеллект
Перейдите вCharacters\Muffin\AIи выберитеAdd New\Artificial Intelligence\Behavior Tree. Назовите егоBT_Muffinи откройте.
Behavior Tree Editor
В редакторе деревьев поведения есть две новые панели:
- Behavior Tree:это граф, в котором мы будем создавать ноды для дерева поведения
- Details:здесь отображаются свойства выбранных нодов
- Blackboard:в этой панели показываются ключи Blackboard (подробнее о них позже) и их значения. Отображается только при запущенной игре.
Как и Blueprint, дерево поведения состоит из нодов. В деревьях поведения бывает четыре типа нодов. Первые два — этоtaskиcomposite.
Что такое Task и Composite? Искусственный интеллект
Как можно понять из названия, task (задача) — это узел, который что-то «делает». Это может быть что-то сложное, например, цепочка комбо, или что-то простое, например, ожидание.
Для выполнения задач нужно использовать композиты (composite). Дерево поведения состоит из множества ветвей (поведений). В корне каждой ветви находится композит. Разные типы композитов имеют разные способы выполнения своих дочерних нодов.
Допустим, у нас есть следующая последовательность действий:
Для последовательного выполнения каждого действия нам нужно использовать композитSequence, потому что Sequence выполняет свои дочерние ноды слева направо. Вот, как это будет выглядеть:
Примечание:всё, что начинается с композита, можно назватьподдеревом. Обычно это поведения. В нашем примереSequence,Move To Enemy,Rotate Towards EnemyиAttackможно считать поведением «атаковать врага».
Если любой из дочерних нодов Sequenceне удаётся выполнить, то Sequence перестаёт выполняться.
Например, если Pawn не может двигаться к врагу, тоMove To Enemyвыполнить не удастся. Это значит, чтоRotate Towards EnemyиAttackне будут выполнены. Однако они выполнятся, если Pawn удастся двигаться к врагу.
Чуть позже мы также узнаем о композитеSelector. Пока мы будем использоватьSequence, чтобы Pawn перемещался в случайную точку, а затем ждал.
Движение в случайную точку
СоздайтеSequenceи соедините его сRoot.
Теперь нам нужно двигать Pawn. СоздайтеMoveToи соедините его сSequence. Этот нод будет двигать Pawn к указанной точке или актору.
Затем создайтеWaitи соедините его сSequence. Необходимо расположить этот нодсправаотMoveTo. Порядок здесь важен, потому что дочерние ноды выполняются слева направо.
Примечание:проверить порядок выполнения можно, посмотрев на числа в правом верхнем углу каждого нода. Чем меньше значения, тем выше приоритет нодов.
Поздравляю, вы только что создали своё первое поведение! Оно будет перемещать Pawn в выбранную точку, а затем ждать пять секунд.
Для движения Pawn нужно указать точку. ОднакоMoveToможет получать значения, полученные только отblackboard, так что давайте его создадим.
Создание Blackboard. Искусственный интеллект
Blackboard — это ресурс, единственное предназначение которого заключается в хранении переменных (называемыхключами (keys)). Можно считать его памятью ИИ.
Хотя использовать их и не обязательно, blackboard обеспечивает удобный способ считывания и сохранения данных. Он удобен, потому что многие ноды в деревьях поведения могут получать только ключи blackboard.
Для создания blackboard вернитесь в Content Browser и выберитеAdd New\Artificial Intelligence\Blackboard. Назовите егоBB_Muffinи откройте.
Blackboard Editor
Редактор blackboard состоит из двух панелей:
- Blackboard:в этой панели отображается список ключей
- Blackboard Details:в этой панели отображаются свойства выбранного ключа
Теперь нам нужно создать ключ, который будет содержать целевую точку.
Создание ключа целевой точки
Поскольку мы храним точки в 3D-пространстве, нам нужно хранить их как векторы. Нажмите наNew Keyи выберитеVector. Назовите егоTargetLocation.
Теперь нам нужен способ генерирования случайной точки и сохранения её в blackboard. Для этого мы воспользуемся третьим типом нодов дерева поведения:service.
Что такое Service?
Services (службы) похожи на задачи (tasks), с помощью которых мы что-то делаем. Однако вместо того, чтобы заставлять Pawn выполнять действия, мы используем службы для выполнения проверок или обновления blackboard.
Службы — это не отдельные ноды, они присоединяются к задачам или композитам. Благодаря этому создаётся более упорядоченное дерево, потому что нам приходится работать с меньшим количеством нодов. Вот как выглядит использование задачи:
А вот как выглядит использование службы:
Теперь мы можем создать службу, генерирующую случайную точку.
Создание службы
Вернитесь кBT_Muffinи нажмите наNew Service.
При этом будет создана и автоматически открыта новая служба. Назовите еёBTService_SetRandomLocation. Чтобы переименовать её, нужно будет вернуться в Content Browser.
Служба должна выполняться только тогда, когда Pawn нужно двигаться. Для этого нужно присоединить её кMoveTo.
ОткройтеBT_Muffinинажмите правой клавишей мышинаMoveTo. ВыберитеAdd Service\BTService Set Random Location.
ТеперьBTService_SetRandomLocationбудет активироваться при активацииMoveTo.
Далее нам нужно генерировать случайную целевую точку.
Генерирование случайной точки
Откройте OpenBTService_SetRandomLocation.
Чтобы узнать, когда активируется служба, создадим нодEvent Receive Activation AI. Он будет выполняться, когда активируется родитель (нод, к которому он прикреплён).
Примечание:существует также событиеEvent Receive Activation, делающее то же самое. Разница между двумя событиями в том, чтоEvent Receive Activation AIтакже предоставляетControlled Pawn.
Для генерирования случайной точки добавим выделенные ноды. ПараметруRadiusприсвоим значение500.
Это даст нам случайную точку в пределах500единиц от Pawn, в которую можно дойти.
Примечание:для определения того, можно ли дойти до точки,GetRandomPointInNavigableRadiusиспользует навигационные данные (которые называютсяNavMesh). В этом туториале я заранее создал NavMesh. Его можно визуализировать, перейдя во Viewport и выбравShow\Navigation.
Если вы хотите создать собственный NavMesh, то создайтеNav Mesh Bounds Volume. Измените его масштаб так, чтобы он ограничивал область, которая должна быть доступна для движения.
Теперь нам нужно сохранять точку в blackboard. Существует два способа выбора используемого ключа:
- Можно указать ключ, использовав его имя в нодеMake Literal Name
- Можно сделать переменную видимой для дерева поведения. Это позволит выбирать ключ из раскрывающегося списка.
Мы воспользуемся вторым способом. Создайте переменную типаBlackboard Key Selector. Назовите еёBlackboardKeyи включитеInstance Editable. Это позволит сделать переменную видимой при выборе службы в дереве поведения.
Далее создадим следующие выделенные ноды:
Подведём итог:
- Event Receive Activation AIвыполняется, когда активируется его родитель (в нашем случае этоMoveTo)
- GetRandomPointInNavigableRadiusвозвращает случайную доступную для навигации точку в радиусе500единиц от управляемого маффина
- Set Blackboard Value as Vectorзадаёт ключу blackboard (передаваемому черезBlackboardKey) значение случайной точки
Нажмите наCompileи закройтеBTService_SetRandomLocation.
Теперь мы хотим сообщить дереву поведения, что нужно использовать наш blackboard.
Выбор Blackboard
ОткройтеBT_Muffinи убедитесь, что ничего не выбрано. Перейдите в панель Details. ВBehavior Treeзадайте дляBlackboard AssetзначениеBB_Muffin.
После этогоMoveToиBTService_SetRandomLocationбудут автоматически использовать первый ключ blackboard. В нашем случае этоTargetLocation.
Наконец, нам нужно приказать контроллеру ИИ запустить дерево поведения.
Выполнение дерева поведения
ОткройтеAIC_Muffinи соединитеRun Behavior TreeсEvent BeginPlay. Выберите дляBTAssetзначениеBT_Muffin.
ТакBT_Muffinбудет запускаться при созданииAIC_Controller.
Нажмите наCompileи вернитесь в основной редактор. Нажмите наPlay, создайте несколько маффинов и посмотрите, как они бродят по экрану.
Нам пришлось потрудиться, но мы справились! Теперь мы должны настроить контроллер ИИ таким образом, чтобы он распознавал врагов в области его видимости. Для этого можно воспользоватьсяAI Perception.
Настройка AI Perception. Искусственный интеллект
AI Perception — это компонент, который можно добавлять к акторам. С его помощью можно давать ИИ (Искусственный интеллект)чувства(такие как зрение и слух).
ОткройтеAIC_Muffinи добавьте компонентAIPerception.
Теперь нам нужно добавить чувство. Мы хотим, чтобы маффин распознавал другого маффина, попадающего в область видимости, так что нужно добавитьзрение.
ВыберитеAIPerceptionи перейдите в панель Details. В разделеAI Perceptionдобавьте кSenses Configновый элемент.
Задайте элемент0дляAI Sight configи разверните его.
У зрения есть три основных параметра:
- Sight Radius:максимальное расстояние, на которое может видеть маффин. Оставим здесь значение3000.
- Lose Sight Radius:если маффин увидел врага, это значение, на которое враг должен отдалиться, чтобы маффин потерял его из виду. Оставим здесь значение3500.
- Peripheral Vision Half Angle Degrees:угол обзора маффина. Задайте значение45. Это даст маффину угол обзора в90градусов.
По умолчанию AI Perception распознаёт только врагов (акторов, назначенных в другуюкоманду). Однако по умолчанию у акторов нет команды. Если актор не имеет команды, то AI Perception считает егонейтральным.
На момент написания статьи не существовало способа назначения команд с помощью Blueprints. Вместо этого можно просто приказать AI Perpcetion распознавать нейтральных акторов. Для этого развернитеDetection by Affiliationи включитеDetect Neutrals.
Нажмите наCompileи вернитесь в основной редактор. Нажмите наPlayи создайте несколько маффинов. Нажмите на клавишу‘для отображения экрана отладки ИИ. Нажмите клавишу4нацифровом блоке, чтобы визуализировать AI Perception. Когда маффин попадает в поле зрения, появляется зелёная сфера.
Теперь нам нужно двигать маффина в сторону врага. Для этого дерево поведения должнознатьо враге. Это можно реализовать сохранением ссылки на врага в blackboard.
Создание ключа врага
ОткройтеBB_Muffinи добавьте ключ типаObject. Переименуйте его вEnemy.
Пока мы не можем использоватьEnemyвMoveTo, потому что ключ имеет типObject, ноMoveToможет получать ключи только типаVectorилиActor.
Чтобы исправить это, выберитеEnemyи развернитеKey Type. Выберите дляBase ClassзначениеActor. Это позволит дереву поведения распознатьEnemyкакActor.
ЗакройтеBB_Muffin. Теперь нам нужно создать поведение для движения в сторону врага.
Движение в сторону врага
ОткройтеBT_Muffinи отделитеSequenceотRoot. Это можно сделать,зажав Alt и нажав левой клавишейнапровод, соединяющий их. Оставим пока поддерево случайного движения в покое.
Теперь создайте выделенные ноды и задайте для ихBlackboard KeyзначениеEnemy:
При этом Pawn будет двигаться кEnemy. В некоторых случаях Pawn не полностью поворачивается к цели, так что нужно использовать ещё иRotate to face BB entry.
Теперь нужно задаватьEnemy, когда AI Perception распознаёт другой маффин.
Задание ключа Enemy
ОткройтеAIC_Muffinи выберите компонентAIPerception. Добавьте событиеOn Perception Updated.
Это событие выполняется при обновлении чувства. В нашем случае оно выполняется, когда ИИ кого-нибудь замечает или теряет из виду. Также это событие передаёт список акторов, которые в данный момент обнаружены чувствами.
Добавьте выделенные ноды. Убедитесь, что дляMake Literal Nameзадано значениеEnemy.
Это позволит проверять, есть ли у ИИ уже враг. Если нет, то мы должны дать ему врага. Для этого нужно добавить выделенные ноды:
Подведём итог:
- IsValidпроверяет, задан ли ключEnemy
- Если он не задан, то циклически обходятся все обнаруженные на данный момент акторы
- Cast To BP_Muffinпроверяет, является ли актор маффином
- Если он маффин, то проверяем, мёртв ли он
- ЕслиIsDeadвозвращаетfalse, то задаём маффина как новогоEnemyи прекращаем цикл
Нажмите наCompileи закройтеAIC_Muffin. Нажмите наPlayи создайте два маффина так, чтобы один находился перед другим. Маффин, расположенный сзади, автоматически начнёт двигаться к другому.
Теперь мы создадим собственную задачу, чтобы маффин выполнял атаку.
Создание задачи атаки
Мы можем создать задачу в Content Browser вместо редактора деревьев поведения. Создайте новыйBlueprint Classи выберите в качестве родительского классаBTTask_BlueprintBase.
Назовите егоBTTask_Attackи откройте. Добавьте нодEvent Receive Execute AI. Этот нод будет выполняться, когда дерево поведения выполняетBTTask_Attack.
Сначала нам нужно заставить маффин атаковать.BP_Muffinсодержит переменнуюIsAttacking. Если она задана, то маффин выполнит атаку. Для этого нужно добавить выделенные ноды:
Если использовать задачу в её нынешнем состоянии, то выполнение остановится на ней, потому что дерево поведения не знает, завершилась ли задача. Чтобы исправить это, добавим к концу цепочкиFinish Execute.
Затем включитеSuccess. Мы используемSequence, поэтому это позволит выполниться нодам послеBTTask_Attack.
Вот как должен выглядеть граф:
Подведём итог:
- Event Receive Execute AIвыполняется, когда дерево поведения запускаетBTTask_Attack
- Cast To BP_Muffinпроверяет, имеет лиControlled PawnтипBP_Muffin
- Если да, то задаётся его переменнаяIsAttacking
- Finish Executeдаёт дереву поведения понять, что задачауспешновыполнена
Нажмите наCompileи закройтеBTTask_Attack.
Теперь нам нужно добавить в дерево поведенияBTTask_Attack.
Добавление атаки в дерево поведения
ОткройтеBT_Muffin. Затем добавьте в конецSequenceнодBTTask_Attack.
Далее добавьте в конецSequenceнодWait. Измените значение его переменнойWait Timeна2. Благодаря этому маффин не будет атаковать постоянно.
Вернитесь в основной редактор и нажмите наPlay. Как и в прошлый раз, создайте два маффина. Маффин начнёт двигаться и поворачиваться к врагу. Затем он атакует и подождёт две секунды. Если он увидит ещё одного врага, то снова повторит ту же последовательность.
В последней части мы объединим поддеревья атаки и случайного движения.
Объединение поддеревьев
Чтобы объединить поддеревья, можно использовать композитSelector. Как и Sequence, он тоже выполняется слева направо. Однако Selector останавливается тогда, когда дочерний нодуспешно выполняется, а не завершается неудачно. С помощью этого поведения мы можем сделать так, что дерево поведения выполнит только одно поддерево.
ОткройтеBT_Muffinи создайтеSelectorпосле нодаRoot. Затем соедините поддеревья следующим образом:
В такой схеме одновременно будет выполняться только одно поддерево. Вот как запускается каждое поддерево:
- Attack:Selectorзапустит первым поддерево атаки. Если все задачи выполнены успешно, тоSequenceтоже завершится успешно.Selectorобнаружит это и остановит выполнение. Благодаря этому поддерево случайного движения выполняться не будет.
- Roam:селектор попытается сначала выполнить поддерево атаки. ЕслиEnemyне задан, тоMoveToзавершится неудачей. ПоэтомуSequenceтоже завершится неудачей. Поскольку поддерево атаки завершилось неудачно,Selectorвыполнит следующий дочерний элемент, то есть поддерево случайного движения.
Вернитесь в основной редактор и нажмите наPlay. Создайте несколько маффинов, чтобы протестировать работу.
Постойте, но почему маффин сразу же не атакует другой маффин?
В традиционных деревьях поведения выполнение начинается при каждом обновлении с корня. Это значит, что при каждом обновлении дерево сначала пробует выполнить поддерево атаки, а затем поддерево случайного движения. Это значит, что дерево поведения может мгновенно менять поддеревья при изменении значенияEnemy.
Однако деревья поведения Unreal работают иначе. В Unreal выполнение продолжается с последнего выполненного нода. Так как AI Perception не чувствует сразу же других акторов, начинает выполняться поддерево случайного движения. Теперь дерево поведения должно дождаться завершения дерева случайного движения, и только потом проверить возможность выполнения поддерева атаки.
Чтобы исправить это, мы можем использовать последний тип нодов:декораторы (decorators).
Создание декоратора. Искусственный интеллект
Как и службы (services), декораторы присоединяются к задачам или композитам. Обычно декораторы используются для выполнения проверок. Если результат равен true, то декоратор тоже возвращает true, и наоборот. Благодаря декораторам мы можем управлять выполнением их родительских элементов.
Кроме того, декораторы обладают возможностьюпрекращенияподдерева. Это значит, что можно завершить выполнение поддерева случайного движения, если заданEnemy. Таким образом маффин сможет атаковать врага сразу же после его обнаружения.
Чтобы использовать прекращение выполнения, мы можем применять декораторBlackboard. Он просто проверяет, задан ли ключ blackboard. ОткройтеBT_Muffinинажмите правой клавишей мышинаSequenceподдерева атаки. ВыберитеAdd Decorator\Blackboard. При этом к Sequence добавится декоратор Blackboard.
Теперь выберите декораторBlackboardи перейдите в панель Details. ЗадайтеBlackboard KeyзначениеEnemy.
Так мы будем проверять, задан лиEnemy. Если он не задан, то декоратор завершится неудачно и приведёт к неудачному завершениюSequence. Это позволит запустить поддерево случайного движения.
Чтобы прекратить выполнение поддерева случайного движения, нам нужно будет использовать параметрObserver Aborts.
Использование Observer Aborts. Искусственный интеллект
Observer aborts прекращает выполнение поддерева при изменении выбранного ключа blackboard. Существует два типа прекращения:
- Self:этот параметр позволяет поддереву атаки прекратить собственное выполнение, когдаEnemyстановится недействительным. Это может случиться, когдаEnemyумирает до завершения поддерева атаки.
- Lower Priority:этот параметр позволяет прекращать выполнение деревьев с более низким приоритетом при заданииEnemy. Поскольку поддерево случайного движения расположено после атаки, оно имеет меньший приоритет.
Выберите дляObserver AbortsзначениеBoth, что включит оба типа прекращения.
Теперь поддерево атаки будет сразу же переходить к случайному движению при отсутствии врага. А поддерево случайного движения немедленно будет переключаться в режим атаки при обнаружении врага.
Вот как выглядит готовое дерево поведения:
Подведём итог поддерева атаки:
- Selectorзапускает поддерево атаки, если заданEnemy
- Если он задан, то Pawn будет двигаться и поворачиваться в сторону врага
- Затем он выполнит атаку
- Наконец, Pawn будет ждать две секунды
Подводим итог поддерева случайного движения:
- Selectorзапускает поддерево случайного движения, если поддерево атаки завершается неудачно. В нашем случае оно завершается неудачно, если не заданEnemy.
- BTService_SetRandomLocationгенерирует случайную точку
- Pawn движется в сгенерированную точку
- Затем он ждёт пять секунд
ЗакройтеBT_Muffinи нажмите наPlay. Создайте несколько маффинов и приготовьтесь к величайшей «Королевской битве»!
Куда двигаться дальше?
Готовый проект можно скачатьотсюда.
Как вы видите, простого ИИ-персонажа создать очень просто. Если вы хотите создавать более сложный ИИ, то изучитеEnvironment Query System. Эта система позволяет ИИ собирать данные об окружении и реагировать на него.
С crazygames вы сможете играть
на каком угодно гаджете, включая ноутбуки, плееры и планшеты.