Galaxy2D Tutorials Галактика 2D
Передвижение врагов

Преследование и отступление

 

Наиболее простой способ заставить монстра следовать за игроком – это двигаться непосредственно к нему, а наиболее простой способ убежать от  него - двигаться в противоположном направлении. Ниже приведен простой алгоритм этого действия, но он функционирует только, при отсутствии каких бы то ни было препятствий.  Алгоритм сравнивает две позиции (монстра и игрока, к примеру), и возвращает направление движения. Но если между ними встанет стена, то монстр просто упрется в нее.

 

#define SIGN(x) ( (x)<0 ? -1 : 1)

             

int GetDir(int Hunter, int Game, int Evade)

{

       if (Hunter == Game)

              return 0;

 

       int i =SIGN(HunterX - GameX);

       if (evade)

              return (i* (-1));

       else

              return i;

}

             

/* остальной код */

             

/* Монстр охотится на игрока, если

 * последний параметр = 0, установите его = 1,

 * чтобы монстр убегал

 */

 MoveX= GetDir(MonsterX, PlayerX, 0);

 MoveY= GetDir(MonsterX, PlayerX, 0);

 

Патрулирование

 

Некоторые игры  допускают патрулирование монстрами местности (движение из точки A,  в точку  B, затем в C и так далее; последняя точка соединяется с первой). Это не намного сложнее, чем предыдущий алгоритм. Единственное различие, что монстры "преследуют" точки пути (waypoints), а не игрока.

 

В той части вашей программы, которая отвечает за перемещение, нужно проверить, достиг ли монстр нужной точки, и если это так, то назначьте ему следующую точку для движения (см. пример 2). Об этом будет еще сказано ниже, при обсуждении движения, которое реализовано в игре Zelda.

 

typedef struct

       {

              int x, y;

       }

       TWaypoint;

 

/* Где-то в программе ... */

 

monster.ActWayPoint = 0;

 

/* ...

 */

 

/* Пока отметка не достигнута

while (! check(monster.Position, monster.WayPoint[ActWayPoint]))

{

/* Двигаемся к ней */

monster.Position.x += GetDir(monster.Position.x, monster.WayPoint[ActWayPoint].x, 0)*speed_x;

monster.Position.y += GetDir(monster.Position.y, monster.WayPoint[ActWayPoint].y, 0)*speed_y;

}

 

/* Поставим целью следующую точку */

monster.ActWayPoint++;

monster.ActWayPoint %= monster.CountWayPoints;

 

Обход препятствий

 

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

 

В зависимости от стороны, с которой произошло столкновение, каждое препятствие возвращает значения пары смещения dx/dy относительно монстра. Правила, для нахождения этих значений, очень просты:

 

·          Допустим, монстр свободно перемещается по экрану, к примеру,  из левой верхней к правой нижней его части. И он соприкоснулся с левой стороной препятствия, тогда пускай он движется вниз.

 

·          Если в препятствие ударились по направлению какой-нибудь оси, возвратим противоположное направление по этой же оси. Если препятствие имеет ИО (-1, 1) и получает удар от чего-то движущегося по оси X, следует возвратить 1.

 

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

 

А вот еще две статьи в тему:

 

·          “Maze navigation” (Навигация в лабиринте)– это название одной очень интересной статьи, которая может быть найдена по адресу: x2ftp tutorial contest . Там рассмотрен метод повторяющихся приближений, дающий некоторый прирост в скорости.

·          Целый сайт под названием "Smart Unit Navigation". Он достаточно хорош, так что я решил рекомендовать его Вам.

 

Примечание:

Можно сформировать набор точек пути для обхода каждого препятствия, тогда монстр, который натолкнется на препятствие, запросит у него, как его обойти. (С.Ю. Анисимов)

 

Движение, реализованное в Zelda

 

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

 

Примечание:

В DOOM реализован немного другой подход: когда игрок попадает в сектор, то все монстры сектора бросаются на него, даже если они его и не видят, то же самое происходит и при выстреле. (С.Ю. Анисимов)

 

Хитрость – отслеживание игрока

 

Один очень эффективный способ достать игрока - использовать "собак": агрессивных, и слабеньких созданий, которые будут следовать за игроком (если они не были бы столь слабы, игра могла бы получиться очень нечестной). Вы можете сохранить направление перемещения игрока как цепочку его позиций на земле, поскольку он стоял в некоторой позиции карты, как атрибут этой позиции. Это дает возможность собаке эффективно следовать за игроком (такой вид алгоритма с успехом можно применять в играх типа pacman).

 

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

 

Верстка и поддержка Lennart Steinke

PMG  12 сентября 2003 (c)  Сергей Анисимов, Сергей Иванов