Pull to refresh

Game Maker AI — С чего начать?

Reading time 8 min
Views 34K
Game Maker — платформа разработки (в начальной концепции 2D) игр. На данный момент наиболее популярны Game Maker 8.0\8.1 и Game Maker Studio. Последний, к слову, поддерживает кросс-платформенную разработку (что, наверное, и является почти единственным фактором выживания движка в целом).

В последнее время GM теряет свою популярность под тенью таких гигантов, как Unity3D/Cryengine/Unreal. Я считаю потерю спроса на этот движок совершенно необоснованной, ведь для создания многих игр (например, под Android) нет необходимости в последних технологиях разработки 3D и других «ненужных» функциях, очень требовательных к системе пользователя. При использовании Game Maker для разработки таких игр можно достичь достаточно высокой производительности без видимых жертв со стороны графики или функционала.

Итак, ближе к делу. С некоторых пор я занимаюсь разработкой игр в среде GM. И в один прекрасный день передо мной возникла интереснейшая задача — разработать приемлемый AI для игры жанра TDS (Top Down Shooter). Признаться, до того момента я не сталкивался с разработкой искусственного интеллекта, и задача поначалу просто поставила меня в тупик. Три последующих дня я провел в поиске решения (естественно, методом проб и ошибок), и узнал я за эти 3 дня довольно много. На основе своего короткого и болезненного опыта, я постараюсь описать основные методы и сторонние библиотеки по разработке AI в среде Game Maker.

Встроенные средства Game Maker

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

Перемещение

mp_potential_step(x,y,stepsize,checkall) //перемещение к точке (x,y)  со скоростью (размером шага) stepsize
//checkall указывает, обрабатывать столкновения только с твердыми объектами (0), или же со всеми (1)
mp_linear_step_object(x,y,stepsize,obj) //аналог предыдущей функции. Различие в том, что столкновения будут обрабатываться лишь с объектами класса obj

Как по мне — очень полезные функции, пусть и далеко не всегда. При их использовании следует учитывать несколько моментов:
Во первых, маску столкновений объекта (параметры устанавливаются в настройках спайта объекта).
Во вторых, категорически не советую использовать данные функции для поиска сложных путей (к примеру, в лабиринте). Как правило, в таких случаях исполнитель «втыкается» в первый же объект и пытается пройти сквозь него.
Данные функции стоит применять при поиске короткого пути на участке без большого количества твердых объектов.

mp_potential_path(path,xg,yg,stepsize,factor,checkall) //функция идентична предыдущим с одним лишь различием - если в прошлых функциях расчет происходит "от шага к шагу", то здесь рассчитывается полный путь до объекта (который записывается в идентификатор path). Функция возвращает истину в том случае, если путь был найден

У данной функции есть одно большое преимущество перед предыдущими — если поиск пути завершится неудачей, движения не происходит. Это оградит исполнителя от «втыкания в стену». Минус же собственно в том, что эта функция возвращает готовый путь, который должен пройти исполнитель. Это нельзя однозначно назвать минусом, но, скажем, во время боя использовать пути крайне накладно и неудобно.

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

И вправду, сетка — очень удобный инструмент при проектировании путей. Она намного более надежна, чем тот же potential_path (поскольку всегда находит путь, если он есть). фыприемов работы с сеткой ушла бы целая статья, поэтому постараюсь ограничиться «минимумом»:

mp_grid_create(left,top,hcells,vcells,cellwidth,cellheight)

Функция создает сетку и возвращаецов
cellwidth,cellheight — размеры ячейки

После создания сетки стоит добавить запрещенные для прохождения зоны. Я пользовался для этого довольно простой функцией:
mp_grid_add_instances(id,obj,prec) //делает "запрещенными" все ячейки, которые пересекает объект с идентификатором obj, prec определяет, делать ли точную проверку для спрайта объекта, id - идентификатор сетки из предыдущей функции

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

Итак, у нас есть сетка с непроходимыми зонами. Теперь дело за малым — рассчитать путь для нашего исполнителя:

mp_grid_path(id,path,xstart,ystart,xgoal,ygoal,allowdiag) //рассчитывает путь из точки (xstart,ystart) в точку (xgoal,ygoal) и записывает его в идентификатор path. allowdiag - может ли объект перемещаться по диагоналям (не только вертикально и горизонтально)


Наш путь готов, если он конечно существует. Минус все тот же — это путь (пусть и более правильный, точный, безотказный путь), и ничего тут не поделаешь.

Первое время мне очень понравился метод сетки, и он имеет право на жизнь, но не в игре моего жанра (в большинстве случаев). В бою этот метод также не применим, как и potential_path. Зато таким образом можно составлять пути для патрулирования вне боя, или, например, поиск игрока после потери из виду.

Определение расстояния и направления

Здесь все довольно просто, за это отвечает всего пара функций:

point_distance(x1,y1,x2,y2)  //возвращает расстояние между точкой (x1,y1) и точкой (x2,y2)
point_direction(x1,y1,x2,y2) //возвращает направление от точки (x1,y1) до точки (x2,y2) в градусах


Конечно, это не единственные функции для работы с расстоянием и направлением, но в большинстве случаев их вполне достаточно. Также в процессе поиска обнаружил любопытный скрипт, определяющий различие между направлениями в градусах (очень полезно при проверке наличия противника в поле зрения исполнителя):

//argument0 - первое направление
//argument1 - второе направление
var diff;
diff = (argument1 - argument0) mod 360;
if diff < 0
    diff += 360;
if abs(diff) > 180
    return (360 - abs(diff)) * -sign(diff);
else
    return diff;


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

Существует несколько (похожих) функций для определения столкновений объектов друг с другом. Вряд ли их можно использовать для определения пути, зато, скажем, при реализации стрельбы (любого вида стрельбы) они просто незаменимы.

collision_point(x,y,obj,prec,notme) //проверяет, есть ли в точке (x,y) столкновение с объектом obj. prec - точная проверка, notme - не проверять вызвавший объект(1)
collision_line(x1,y1,x2,y2,obj,prec,notme) //похожая функция. Разница лишь в том, что столкновение проверяется на прямой от точки (x1,y1) до (x2,y2) 
collision_circle(xc,yc,radius,obj,prec,notme) //определяет наличие столкновения внутри окружности с центром в точке (xc,yc) и радиусом radius


На мой взгляд, данные функции не нуждаются в дополнительном объяснении. Могу лишь сказать, что ими удобно пользоваться при реализации стрельбы, или радара, или некой системы наведения для AI.

Сторонние библиотеки

Наконец-то, встроенные средства GM позади, и можно пуститься в свободное плавание — в мир библиотек. Хотя не все настолько радужно, ибо библиотек для разработки AI не так уж много. Попробую перечислить все, что мне удалось найти.

TDS AI Lib

TDS AI — незамысловатое название для библиотеки, предназначенной для разработки AI в играх жанра TDS. На деле же это просто набор достаточно часто используемых функций, встраиваемых прямо в интерфейс GM.

Функционал TDS AI:


В обращении библиотека достаточно проста (при минимальных знаниях английского разумеется). С помощью данной библиотеки исполнителя можно «научить» таким вещам, как поворот в сторону объекта, следование за объектом, уклонение от объекта (возможно, имеет смысл использовать даже для уклонения от выстрелов), проверка расстояния до объекта и наличие его в поле зрения, а также random-ное перемещение.

На этом и заканчивается функционал библиотеки. Вердикт: удобно, просто в использовании, но недостаточно. Ведь AI — не просто набор функций.
*ссылку на библиотеку можно найти в конце статьи

Behaviour Tree AI

После TDS AI я наткнулся еще на несколько библиотек и примеров, но ни один из них не заслужил внимания в этой статье. Честно говоря, я был в отчаянии — неужели мне придется самому разрабатывать систему AI (удобную, расширяемую систему)? От подобной мысли веяло ужасом, поскольку у меня не было ровно никакого опыта в разработке искусственного интеллекта.

Но вот случилось чудо, и совершенно случайно я наткнулся на этот шедевр. По началу я не до конца понял его смысл, но мне стало интересно. Я скачал исходник с примером, и после получаса изучения кода я понял, что ко мне попало нечто действительно уникальное. Behaviour Tree — не просто набор полезных функций, это целая система для разработки AI. Именно здесь реализован структурный подход к созданию ИИ, необходимый для удобной работы и расширения.

На описание возможностей BT AI уйдет целая статья. Тем более, такая статья уже есть, ссылку на нее можно найти в конце моей статьи. Исходя из этого, я постараюсь объяснить лишь основы.

Итак, в чем же суть? Как я уже говорил, суть в структурном подходе. AI, основанный на данной библиотеке, строится по следующей схеме:
  1. Выбирается исполнитель
  2. Создается главный скрипт AI. В данном скрипте и происходит выполнение функций в зависимости от условий
  3. Создается скрипт обновления, необходимый для постоянного обновления параметров, используемых в главном скрипте (например, в бою ли исполнитель, его HP и другие параметры)
  4. В событии создания исполнителя устанавливаются главный скрипт и скрипт обновления
  5. В событии шага (своеобразный постоянный таймер) исполнителя вызывается функция повторения скрипта


Главный скрипт работает по системе выбор-условие-действие (selector-condition-action).
  • Выбор — своеобразный аналог оператора switch() в языках программирования. В «теле» элемента выбора происходит выбор одного из заданных наборов действий в зависимости от условия.
  • Условие — проверка истинности заданного условия. Используется в начале каждого набора действий внутри элемента выбора. Если условие истинно — выполняется данный набор действий и выбор завершается. Если нет — происходит переход к следующему набору действий
  • Действие — скрипт, запускающий другой скрипт (действие) с заданными параметрами. *В BT AI существует понятие базовых действий. Подробнее о них можно узнать в той же статье по ссылке ниже.


Небольшой пример главного скрипта BT AI:

SelectorBegin('AI Role 1');
 SequenceBegin('Атака'); 
               //видим врага и знаем его id 
               Condition(SeeEnemy && Enemy>0); 
               //смомтрим на него 
               Action( Action_LookAt, point_direction(x,y, Enemy.x, Enemy.y)); 
               //стреляем в сторону врага 2 раза 
               Action( Action_Shoot, point_direction(x,y, Enemy.x, Enemy.y), 2); 
               SelectorBegin('Подходим на оптимальное растояние'); 
                   //или 
                   SequenceBegin('Враг слишком далеко'); 
                       Condition(point_distance(x,y, Enemy.x, Enemy.y)>256); 
                       Action(Action_MoveTo, Enemy.x-lengthdir_x(128, direction), Enemy.y-lengthdir_y(128, direction), highSpeed); 
                   SequenceEnd(); 
                   //или 
                   SequenceBegin('Враг слишком близко'); 
                       Condition(point_distance(x,y, Enemy.x, Enemy.y)<64); 
                       //идем назад 
                       Action(Action_MoveTo, x-lengthdir_x(64, direction), y-lengthdir_y(64, direction), highSpeed); 
                   SequenceEnd(); 
                   SequenceBegin('маневр'); 
                       //иначе просто маневрируем, чтобы сложнее было попасть                   
                       Action( Action_MoveTo, x+irandom_range(-64, 64), y+irandom_range(-64, 64), highSpeed); 
                   SequenceEnd(); 
               SelectorEnd(); 
               //стреляем в сторону врага 4 раза 
               Action(Action_Shoot, point_direction(x,y, Enemy.x, Enemy.y), 2); 
           SequenceEnd(); 
SelectorEnd();


Selector — оператор выбора набора действий
Sequence — набор действий
Condition — проверка условия
Action — действие. вызов скрипта(первый аргумент) с параметрами (остальные аргументы)

Заключение

Напоследок готов повторить, что Behaviour Tree — лучшая моя находка в сфере AI для Game Maker, и я горячо советую ее к использованию. Именно на ней реализован тот самый AI, который мне нужно было сделать.
Глупо будет отрицать, что существует множество других систем для разработки искусственного интеллекта, и многие из них могли просто пройти мимо моего внимания. Буду очень рад ценным дополнениям к моей статье, а также указаниям на мои ошибки, если они есть.

Благодарю за внимание!

Ссылки

Tags:
Hubs:
+11
Comments 11
Comments Comments 11

Articles