Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Unity_в_действии_Джозеф_Хокинг_Рус.pdf
Скачиваний:
83
Добавлен:
21.06.2022
Размер:
26.33 Mб
Скачать

168      Глава 7. Игра от третьего лица: перемещение и анимация игрока

ПРИМЕЧАНИЕ  Наиболее математически одаренные читатели могут подумать: «Нельзя ли воспользоваться преобразованиями от одной координатной системы к другой, о которых шла речь в главе 2?» Разумеется, мы можем преобразовать положение точки смещения, воспользовавшись углами Эйлера, но для этого сначала нужно будет перейти к другой системе координат. Намного проще без этого обойтись.

Завершает код метод LookAt(), направляющий камеру на целевой объект; он предназначен для нацеливания одного объекта (не обязательно камеры) на другой. Вычисленная ранее величина поворота используется для размещения камеры под нужным углом к целевому объекту, но при этом камера уже не поворачивается, меняется только ее положение. То есть без заключительной строки с методом LookAt камера летала бы вокруг персонажа, но при этом смотрела бы в разные стороны. Попробуйте превратить эту строку в комментарий и посмотреть, что получится.

Итак, мы написали сценарий, перемещающий камеру вокруг персонажа; пришла очередь кода, перемещающего персонаж по сцене.

7.2. Элементы управления движением, связанные с камерой

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

ЧТО ОЗНАЧАЕТ «СВЯЗАННЫЕ С КАМЕРОЙ»?

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

сточки зрения объекта может не совпадать с «лево» в общем смысле. Аналогичная ситуация возникает с выражением «персонаж бежит налево». Оно может подразумевать как левую сторону

сточки зрения персонажа, так и левую сторону экрана.

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

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

Реализация связанных с камерой элементов управления состоит из двух этапов: сначала мы ориентируем персонажа в соответствии с положением элементов управления, а затем двигаем его вперед. Давайте напишем соответствующий код.

7.2.Элементы управления движением, связанные с камерой      169

7.2.1.Поворот персонажа лицом в направлении движения

Начнем мы с написания кода, поворачивающего персонажа лицом в направлении клавиш со стрелками. Создайте сценарий RelativeMovement на C# (листинг 7.2). Перетащите этот сценарий на объект player и свяжите камеру со свойством Target компонента Script (таким же образом, как вы связывали персонажа со свойством Target сценария камеры). После этого персонаж при нажатии управляющих клавиш будет поворачиваться в разные стороны, выбирая направление относительно камеры, а при его вращении с помощью мыши он останется статичным.

Листинг 7.2. Поворот персонажа относительно камеры

using UnityEngine;

using System.Collections;

public class RelativeMovement : MonoBehaviour {

[SerializeField] private Transform target; ¬ Сценарию нужна ссылка на объект, относительно которого будет происходить перемещение.

void Update() {

Vector3 movement

= Vector3.zero; ¬ Начинаем с вектора (0, 0, 0), непрерывно добавляя компоненты движения.

float

horInput =

Input.GetAxis("Horizontal");

float

vertInput

=

Input.GetAxis("Vertical");

if (horInput !=

0

|| vertInput != 0) { ¬ Движение обрабатывается только

movement.x = horInput;

при нажатии клавиш со стрелками.

movement.z = vertInput;

 

 

Сохраняем начальную ориентацию, чтобы вернуться

Quaternion tmp = target.rotation; ¬ к ней после завершения работы с целевым объектом.

target.eulerAngles = new Vector3(0, target.eulerAngles.y, 0);

movement = target.TransformDirection(movement); ¬ Преобразуем направление движения

target.rotation = tmp;

из локальных в глобальные координаты.

 

Метод LookRotation() вычисляет

transform.rotation = Quaternion.LookRotation(movement); ¬ кватернион, смотрящий в этом

}

направлении.

}

}

Аналогично коду листинга 7.1, код этого листинга начинается с сериализованной переменной для целевого объекта. Если в предыдущем случае нам требовалась ссылка на объект, вокруг которого будет совершаться облет, то теперь нужна ссылка на объект, относительно которого будет выполняться перемещение. Затем мы переходим к методу Update(). Его первая строка объявляет, что Vector3 имеет значение 0, 0, 0. Важно взять нулевой вектор и затем присвоить ему значения, а не просто создать вектор с вычисленными значениями перемещения. Дело в том, что величины перемещения по вертикали и горизонтали вычисляются на разных этапах, но при этом должны быть частями одного и того же вектора.

Далее мы проверяем элементы управления вводом, как делали это в предыдущих сценариях. Именно здесь вектору движения присваиваются значения координат X и Z, определяющие горизонтальные перемещения по сцене. Помните, что метод Input. GetAxis() возвращает значение 0, если ни одна клавиша не нажата, а при нажатии клавиш со стрелками его значение меняется от 1 до –1. Присваивание этого значения

170      Глава 7. Игра от третьего лица: перемещение и анимация игрока

вектору движения задает перемещение в положительном или отрицательном направлении оси (в случае оси X это перемещение влево и вправо, в случае оси Z — вперед и назад).

В следующих нескольких строках вектор движения корректируется относительно камеры. А именно метод TransformDirection() выполняет преобразование локальных координат в глобальные. Мы уже пользовались им для этой цели в главе 2, но на этот раз мы совершаем переход от системы координат целевого объекта, а не игрока. При этом код до и после строки с методом TransformDirection() выравнивает систему координат нужным нам образом: первым делом сохраняется ориентация целевого объекта, чтобы потом к ней можно было вернуться, а затем преобразование поворота корректируется таким образом, чтобы оно совершалось только относительно оси Y, а не всех трех осей. После чего мы выполняем преобразование и восстанавливаем ориентацию целевого объекта.

Весь этот код вычисляет направление движения в виде вектора. Последняя строка применяет полученный результат к персонажу, преобразуя Vector3 в Quaternion методом Quaternion.LookDirection() и присваивая это значение. Попробуйте запустить игру и посмотреть, что получится!

ПЛАВНОЕ ВРАЩЕНИЕ С ПОМОЩЬЮ АЛГОРИТМА ЛИНЕЙНОЙ ИНТЕРПОЛЯЦИИ

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

public float rotSpeed = 15.0f;

Затем замените строку transform.rotation... в конце листинга 7.2 следующим кодом:

...

Quaternion direction = Quaternion.LookRotation(movement); transform.rotation = Quaternion.Lerp(transform.rotation, direction, rotSpeed * Time.deltaTime);

}

}

}

В результате вместо непосредственной привязки к значению, возвращаемому методом LookRotation(), это значение начнет использоваться в качестве целевого положения, в которое объект будет постепенно поворачиваться. Метод Quaternion.Lerp() выполняет плавный поворот из текущего положения в целевое (третий параметр метода контролирует скорость вращения).

Кстати, плавный переход от одного значения к другому называется интерполяцией; интерполировать можно значения любых типов. Термин Lerp расшифровывается как linear interpolation — линейная интерполяция. В Unity есть методы Lerp как для векторов, так и для значений типа float (то есть вы можете интерполировать положение объектов, цвета и другие параметры). Для кватернионов имеется также родственный метод Slerp (метод сферической линейной интерполяции). Для поворотов на маленькие углы преобразования Slerp зачастую подходят лучше, чем Lerp.

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

7.2. Элементы управления движением, связанные с камерой      171

ПРИМЕЧАНИЕ  Так как поворотом налево и направо управляют клавиши, отвечающие за облет камеры, поворот персонажа в сторону будет сопровождаться медленным вращением. Подобное дублирование функций элементов управления является желательным поведением для данного проекта.

7.2.2. Движение вперед в выбранном направлении

Как вы помните из главы 2, для перемещения персонажа по сцене к нему нужно добавить соответствующий контроллер. Выделите персонажа и выберите в меню

Components команду Physics Character Controller. На панели Inspector уменьшите параметр Radius до 0.4, остальным же параметрам оставьте значения, предлагаемые по умолчанию, так как они вполне подходят для нашей модели персонажа.

Следующий листинг содержит код, который нужно добавить в сценарий Relative­ Movement.

Листинг 7.3. Код, меняющий положение персонажа

using UnityEngine;

using System.Collections;

[RequireComponent(typeof(CharacterController))] ¬

Окружающие строки показывают контекст

public class RelativeMovement : MonoBehaviour {

размещения метода RequireComponent().

 

...

 

public float moveSpeed = 6.0f;

 

private CharacterController _charController;

 

void Start() {

Этот паттерн, знакомый вам

_charController = GetComponent<CharacterController>(); ¬ по предыдущим главам, используется

}

 

 

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

void Update() {

 

 

 

...

 

Переписываем существующие строки для X и Z,

movement.x = horInput * moveSpeed; ¬

movement.z = vertInput * moveSpeed;

чтобы добавить скорость движения.

 

Ограничиваем движение

movement = Vector3.ClampMagnitude(movement, moveSpeed);

¬ по диагонали той же скоростью,

...

 

 

что и движение вдоль оси.

}

 

 

 

movement *= Time.deltaTime; ¬

Не забывайте умножать перемещения

 

на значение deltaTime, чтобы они

 

_charController.Move(movement); не зависели от частоты кадров.

}

}

Запустив игру, вы увидите, как персонаж (в T-образной позе) перемещается по сцене. Практически весь код этого листинга вы уже видели, поэтому я ограничусь кратким обзором.

В первую очередь, в верхней части кода появился метод RequireComponent(). Как объяснялось в главе 2, этот метод заставляет Unity проверять наличие у объекта GameObject компонента переданного в команду типа. Хотя эта строка не обязательна, без указанного компонента в сценарии появятся ошибки.