Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming / GameProg / RPG_Programming_2ed.pdf
Скачиваний:
248
Добавлен:
12.02.2016
Размер:
12.06 Mб
Скачать

Глава 12. Управление игроками и персонажами

Ходьба по маршруту

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

Использование маршрутных точек

Маршрутные точки определяются как набор координат, и чтобы сохранить привычные трехмерные концепции, можно для их хранения использовать следующую структуру:

typedef struct sRoutePoint { float XPos, ZPos;

} sRoutePoint;

Для создания маршрута вы выбираете точки, которые должен пройти персонаж и формируете массив структур sRoutePoint для хранения

координат. На рис. 12.4 показана простая карта с пятью отмеченными точками.

sRoutePoint Route[5] = {

{-200.0f, -100.0f },

{100.0f, -300.0f },

{300.0f, -200.0f },

{200.0f, 100.0f },

{0.0f, 400.0f }

};

long NumRoutePoints = 5; // Чтобы проще было узнать количество точек

Рис. 12.4. На воображаемой карте показано пять маршрутных точек. Персонаж начинает с точки 1 и идет прямо к точке 2, а затем к точек 3 и так до тех пор, пока не достигнет точки 5. После этого персонаж возвращается к точке 1 и начинает обход маршрута снова

532

netlib.narod.ru

Джим Адамс

Ходьба от точки к точке

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

Начнем с предположения, что координаты персонажа хранятся в следующих переменных (вместе со скоростью перемещения персонажа):

float CharXPos, CharZPos; // Координата Y не нужна float WalkSpeed; // Скорость ходьбы за кадр

Далее предположим, что вы уже установили координаты, в которые хотите переместить персонаж, и поместили их в другую пару переменных:

float RouteXPos, RouteZPos; // Снова без координаты Y

Теперь начинаем перемещение персонажа, вычисляя переменные передвижения:

//Вычисляем расстояние от персонажа до маршрутной точки float XDiff = (float)fabs(RouteXPos - CharXPos);

float ZDiff = (float)fabs(RouteZPos - CharZPos); float Length = sqrt(XDiff*XDiff + ZDiff*ZDiff);

//Вычисление перемещения к пункту назначения

float MoveX = (RouteXPos - CharXPos) / Length * WalkSpeed; float MoveZ = (RouteZPos - CharZPos) / Length * WalkSpeed;

Теперь, всякий раз, когда вы обновляете местоположение персонажа в кадре, вам необходимо прибавить MoveX и MoveZ к координатам

персонажа:

CharXPos += MoveX; CharZPos += MoveZ;

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

Быстрее Пифагора

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

Чтобы увидеть, что я подразумеваю, взгляните на следующие две строки кода:

netlib.narod.ru

533

Глава 12. Управление игроками и персонажами

float Distance = sqrt(Length1*Length1 + Length2*Length2); float Distance = Length1*Length1 + Length2*Length2;

ПРИМЕЧАНИЕ

Теорема Пифагора, возможно, самая известная теорема в

 

геометрии. Она утверждает, что квадрат длины гипотенузы

 

в прямоугольном треугольнике равен сумме квадратов

 

длин катетов. Фактически, это означает, что квадратный

 

корень длин двух сторон (они должны быть возведены в

 

квадрат и сложены) равен длине третьей стороны

 

прямоугольного треугольника. Правда, просто?

Обратите внимание, что показанные две строки кода практически идентичны, за исключением того, что во второй строке опущена функция sqrt, из-за чего она выполняется гораздо быстрее. Недостаток в том, что вы не получаете точную длину, но это не представляет проблемы.

Например, вы измеряете расстояние между двумя точками, и хотите видеть, меньше ли оно 40. Если координаты этих двух точек 0, 0 и 30, 20, более быстрое вычисление даст вам расстояние 1 300 (поскольку длины двух сторон 30 и 20, соответственно).

Как теперь определить расстояние? Вычислив квадрат (произведение на само себя) расстояния, вот как! Умножив 40 на 40 вы получаете 1 600. Сравнив вычисленное расстояние между точками, 1 300, вы видите, что оно меньше, чем 1 600 и, следовательно, меньше, чем оригинальное расстояние 40, которое вы проверяете.

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

BOOL TouchedRoutePoint(float CharXPos, // Координаты персонажа float CharZPos,

float RouteXPos, // Координаты маршрутной точки float RouteZPos,

float Distance) // Проверяемое расстояние

{

//Вычисляем квадрат расстояния для проведения быстрой проверки

Distance *= Distance;

//Вычисляем расстояние

float XDiff = (float)fabs(RouteXPos - CharXPos); float ZDiff = (float)fabs(RouteZPos - CharZPos); float Dist = XDiff*XDiff + ZDiff*ZDiff;

 

// Возвращаем результат

 

if(Dist <= Distance) // Внутри проверяемого диапазона

 

return TRUE;

 

return FALSE; // Вне диапазона расстояний

 

}

 

 

534

netlib.narod.ru

Джим Адамс

При вызове TouchedRoutePoint с координатами персонажа,

координатами маршрутной точки и проверяемым расстоянием от точки, вы получаете значение TRUE, если персонаж находится в пределах Distance

единиц от маршрутной точки. Возвращаемое значение FALSE свидетельствует, что от персонажа до маршрутной точки больше Distance

единиц.

Прохождение маршрута

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

sRoutePoint Route[5] = {

{-200.0f, -100.0f },

{100.0f, -300.0f },

{300.0f, -200.0f },

{200.0f, 100.0f },

{0.0f, 400.0f }

};

long NumRoutePoints = 5;

// Координаты персонажа и переменные перемещения float CharXPos = Route[0].XPos;

float CharZPos = Route[0].ZPos; float MoveX, MoveZ;

float Speed; // Скорость ходьбы персонажа

//Начинаем отслеживание до второй точки long TargetRoutePoint = 1; SetupMovement(TargetRoutePoint);

//Бесконечный цикл перемещения

//и проверки достижения маршрутной точки while(1) {

//Персонаж находится в пределах маршрутной точки? if(TouchedRoutePoint(TargetRoutePoint, 32.0f) == TRUE) {

//Переходим к следующей маршрутной точке

TargetRoutePoint++; if(TargetRoutePoint >= NumRoutePoints)

TargetRoutePoint = 0;

SetupMovement(TargetRoutePoint);

}

// перемещаем персонаж

CharXPos += MoveX; CharZPos += MoveZ;

}

// Функция проверки нахождения в пределах маршрутной точки

BOOL TouchedRoutePoint(long PointNum, float Distance)

{

Distance *=

Distance;

 

float

XDiff

= (float)fabs(CharXPos - Route[PointNum].XPos);

 

float

ZDiff

= (float)fabs(CharZPos - Route[PointNum].ZPos);

 

 

 

 

 

 

 

netlib.narod.ru

535

Соседние файлы в папке GameProg