Камера от первого лица

Xtreme3D

Урок 4
Камера от первого лица

Уровень: начинающий
Версия Xtreme3D: 3.0.x
Автор урока: Gecko

На уроке 3 мы рассмотрели простейший вариант камеры от первого лица - она управлялась клавишами-стрелками. Между тем, в абсолютном большинстве современных игр в этом случае используется управление мышью. Давайте рассмотрим, как реализовать его средствами Xtreme3D.

Начнем с того, что создадим родительский Манекен для камеры - camPos. Мы будем двигать не камеру, а его.

camPos = DummycubeCreate(global.scene);
ObjectSetPosition(camPos, 0, 2, 0);
camera = CameraCreate(camPos);
ViewerSetCamera(view1, camera);

Дело в том, что камера должна двигаться только в плоскости XZ - иными словами, не должна "летать" по воздуху. Мы будем поворачивать объект camPos по оси Y, когда пользователь сместит мышь по горизонтали - таким образом, можно будет управлять направлением движения. Смещение мыши по вертикали вызовет локальный поворот объекта camera по оси X - таким образом, пользователь сможет смотреть вверх и вниз, но это никак не повлияет на направление движения, ведь camera наследует движение от camPos.

Объявим также следующие переменные:

centerX = display_get_width() / 2;
centerY = display_get_height() / 2;

Это координаты центра экрана. Мы будем считывать смещение мыши относительно этой точки, а затем возвращать в нее курсор.

Можно также сразу поместить курсор в центр экрана, чтобы на начало игры камера смотрела строго вперед:

display_mouse_set(centerX, centerY);

Теперь переходим к событию Step. Следующий код вычисляет смещение курсора мыши относительно центра экрана и поворачивает camPos и camera на соответствующие углы, deltaX и deltaY:

deltaX = (centerX - display_mouse_get_x()) / 3;
deltaY = (centerY - display_mouse_get_y()) / 3;
ObjectRotate(camera, deltaY, 0, 0);
ObjectRotate(camPos, 0, -deltaX, 0);
display_mouse_set(centerX, centerY);

Осталось реализовать движение. Мы будем использовать стандартную для игр от первого лица раскладку WASD:

dt = 1.0 / room_speed;
if keyboard_check(ord('W')) ObjectMove(camPos, -10 * dt);
if keyboard_check(ord('A')) ObjectStrafe(camPos, 10 * dt);
if keyboard_check(ord('D')) ObjectStrafe(camPos, -10 * dt);
if keyboard_check(ord('S')) ObjectMove(camPos, 10 * dt);

Смысл умножения на dt в следующем. Если двигать объекты с фиксированной скоростью, их фактическая скорость движения будет привязана к кадровой частоте приложения. То есть, например, если мы двигаем объект на 10 единиц за кадр, скорость при частоте в 60 FPS будет равняться 10 * 60 = 600 единицам в секунду. При частоте 120 FPS, соответственно - 10 * 120 = 1200. В итоге, объект будет двигаться быстрее или медленнее, в зависимости от FPS. Это совсем не то, что нам нужно, поэтому нужно задавать скорость в других величинах, не привязанных к кадру. Например - в единицах в секунду. Следовательно, кадровая скорость будет равна V / FPS, где V - скорость. Мы просто выясняем, на сколько объект должен переместиться за один кадр, если в секунду он перемещается на V единиц. Таким образом, объект будет двигаться с правильной скоростью при любой кадровой частоте.

Чтобы не загромождать код делениями (деление, как известно, относительно медленная операция), мы вместо этого умножаем скорость на 1 / FPS - это значение можно рассчитать только один раз. Оно также называется шагом времени (именно этот шаг времени следует передавать в функцию Update, о чем говорилось на уроке 2). В Game Maker 8 кадровая частота (FPS) обычно фиксирована и задается в настройках комнаты (Room speed). Ее можно выставить равной 60 или 120.