. Искусственный интеллект Half-Life SDK: ретроспектива
Искусственный интеллект Half-Life SDK: ретроспектива

Искусственный интеллект Half-Life SDK: ретроспектива

На момент выпуска в 1998 году Half-life получил тёплый приём за свой гейм-дизайн, который стал возможным благодаря искусственному интеллекту. Это влияние AI привело к тому. что HL назвали одной из самых важных игр в истории.

И даже двадцать лет спустя, изучив её код, можно многое узнать о создании простых, но эффективных систем AI. Вся логика AI жёстко закодирована на C++ и не слишком объектоориентирована, поэтому в ней гораздо легче разобраться, чем в более свежих движках (хотя и расширять её не так просто).

В этой статье мы рассмотрим открытый SDK для Half-Life 1, проанализируем различные аспекты AI, такие как система планировщика задач, её реализация, похожая на конечные автоматы, и сенсорная система. Прочитав статью, вы глубже поймёте принцип использования этих концепций и их реализации в играх.

Скриншот 1: охранник Барни сражается с одним из монстров

Загрузка Half-Life SDK

Установить SDK Valve для Half-Life очень просто (с отличие от инструментов F.E.A.R.) и если вы хотите разрабатывать моды, то для него требуется только оригинальная игра. Вот, что вам будет нужно:

  1. Скачайте версию 2.3 SDK Half-Life, или только исходники без ресурсов, или копию полного SDK с моделями.
  2. Распакуйте файл в любой каталог, лучше в папку с игрой, если вы хотите разрабатывать с помощью SDK моды. Это займёт несколько секунд, в результате у вас будет пачка каталогов с моделями и исходным кодом.

Скриншот 2: код игры на C++ в SDK Half-Life версии 2.3.

Разбираемся с кодом

Кодовая база не так хорошо структурирована, как в F.E.A.R. или даже в Quake 3. В ней есть несколько подкаталогов, но файлы имеют не очень понятные названия, а реализация классов C++ разбросана по нескольким файлам, из названий которых почти ничего нельзя понять.

  • В полном SDK есть две папки, в которых содержится код: Single-Player Source и Multiplayer Source . Обе они имеют схожую структуру каталогов.
  • Бо́льшая часть игровой логики находится в подкаталоге /dll/ , в котором содержатся все файлы, необходимые для сборки hl.dll, который также является фреймворком для модов. Кроме того, в этом каталоге содержится код ИИ, разбросанный по множеству файлов, с названиями типа *monster*.[h,cpp] , *ai*.[h,cpp] и других файлах
  • В каталоге с исходным кодом есть и другие каталоги, например engine , в котором содержатся файлы заголовков, взаимодействующие с основным исполняемым файлом (как базовые сущности). В каталоге common также содержатся похожие низкоуровневые файлы, используемые движком и кодом игры.

Скриншот 3: катсцена из игры с учёным.

Планировщик и система целей

В файлах schedule.[h,cpp] находится очень простая система, управляемая целями. Она состоит из нескольких уровней задач, которые можно процедурно объединять.

Задачи

Задачи — это короткие атомизированные поведения, имеющие конкретное назначение. Например, большинство акторов Half-Life поддерживает следующие задачи: TASK_WALK_PATH , TASK_CROUCH , TASK_STAND , TASK_GUARD , TASK_STEP_FORWARD , TASK_DODGE_RIGHT , TASK_FIND_COVER_FROM_ENEMY , TASK_EAT , TASK_STOP_MOVING , TASK_TURN_LEFT , TASK_REMEMBER . Они определяются как перечисления в файле заголовка и реализуются как методы C++.

Условия

Условия используются для выражения ситуации актора в мире. Поскольку логика задана жёстко, условия можно выразить очень компактно, как битовые поля, но в таком случае условий может быть не больше 32. Например, условиями являются COND_NO_AMMO_LOADED , COND_SEE_HATE , COND_SEE_FEAR , COND_SEE_DISLIKE , COND_ENEMY_OCCLUDED , COND_ENEMY_TOOFAR , COND_HEAVY_DAMAGE , COND_CAN_MELEE_ATTACK2 , COND_ENEMY_FACING_ME .

Планы

План состоит из серии задач (с произвольными параметрами) и учитывает битовое поле условий, чтобы определить, когда план неприменим. Для удобства отладки объекты планов имеют имена.

Цели находятся на более высоком уровне и состоят из планов. Логика цели может при необходимости выбирать план на основании проваленной задачи и текущего контекста. Примеры целей из Half-Life: GOAL_ATTACK_ENEMY , GOAL_MOVE , GOAL_TAKE_COVER , GOAL_MOVE_TARGET и GOAL_EAT .

Использованный Valve код извлечён из движка Quake, и до сих пор достаточно очевиден, несмотря на то, что был преобразован в C++; файлы и struct имеют похожие названия.

Скриншот 4: десантники подняли тревогу в исследовательском центре.

Конечный автомат

На практике все эти планы и задачи соединены вместе в структуру, похожую на конечный автомат. На верхнем уровне для обновления ИИ вызывается функция в monsterstate.cpp :

Она, в свою очередь, вызывает перегруженные функции, отвечающие за проверку с помощью MaintainSchedule() применимости текущего плана и выбор новых с помощью GetSchedule() . Их можно изменять в зависимости от потребностей с помощью порождённых классов, см., например, barney.cpp или scientist.cpp .

На нижнем уровне функции StartTask() и RunTask() реализуют логику для каждого из идентификаторов задач, определённых в конструкции enum . Они реализованы в классах, тоже унаследованных из CBaseMonster . В результате это во многом выглядит как конечный автомат, реализованный как конструкция switch .

Более типичным подходом была бы реализация каждого из этих блоков case в их собственном классе, но при существующей реализации гораздо проще при необходимости использовать логику одного объекта в другом, хотя и ценой модульности.

Интересно также заметить, что AI хранит два состояния: одно идеальное и одно текущее. Таким образом коду игры проще создавать для акторов цели, и заставлять их находить наилучшие способы их достижения. Это интересное сочетание конечного автомата и целенаправленной системы.

Скриншот 5: игровая катсцена с учёным.

Реализация сенсорной системы

В базовом monster.[h,cpp] есть код, дающий всем акторам зрение, обоняние и слух.

Функция зрения проверяет различные флаги, такие как SF_MONSTER_PRISONER и SF_MONSTER_WAIT_TILL_SEEN , чтобы при необходимости обеспечивать дизайнерам возможность контроля. В уравнении также учитываются такие параметры, как область видимости и угол обзора.

Код слуха и обоняния работает похожим образом, только использует события звука. Хранится список объектов, требующих внимания монстров, а сенсорная система выбирает для фокусировки лучший из них.

Итоги и дополнительное чтение

В целом, исходный код, стоящий за этой системой, хотя и прост, очень информативен. Если вы хотите подобрать лёгкую реализацию принятия решений искусственным интеллектом, то стоит выбрать этот подход. Однако, возможно, стоит реализовать каждую задачу в своём собственном объекте: в наши дни в коммерческих играх обычно используют такое решение.

📎📎📎📎📎📎📎📎📎📎