Живая демонстрация: https://webgl- Third-Person-controller.vercel.app/

Управление

  • W — двигаться вперед
  • S — двигаться назад
  • A — поверните налево
  • D — поверните направо
  • Shift + W — Бежать вперед 🚀

Для прохождения этого пошагового руководства необходимы базовые знания javascript и Three.js. Это не учебник по Three.js, я сосредоточусь на самой механике управления персонажем.

Официальная документация Three.js действительно хороша и содержит множество полезных примеров, которые я настоятельно рекомендую прочитать.

  1. Подготовка

Прежде всего, мы загрузим модель и добавим ее в сцену, а затем настроим перспективную камеру на смотреть персонажа. Также нам нужно извлечь анимацию и назначить ее объекту action, который мы собираемся использовать позже для анимации нашего персонажа. Для персонажа я использовал бесплатную модель, скачанную с Mixamo.

Анимации были объединены и преобразованы в формат gltf в Blender, я рекомендую следовать этому руководству: https://www.donmccurdy.com/2017/11/06/creating-animated-gltf-characters-with-mixamo- и-блендер/

Анимация по умолчанию настроена на холостой ход, и камера смотрит на модель. Хорошее начало.

2. Управление персонажем

Пожалуй, самая простая часть. Процесс довольно прост. Нам нужно добавить eventListener на события нажатия клавиш и перевести символ по оси Z.

По умолчанию мы устанавливаем скорость на ноль: пусть скорость = 0,0, это состояние бездействия. И создайте переменную для скорости: let speed = 0;

После ряда экспериментов я решил установить скорость 0,09 для ходьбы и 0,2 для бега и отрицательные значения для движения назад. Не стесняйтесь настраивать параметры по желанию для достижения предпочтительной скорости движения.

Теперь нам нужно добавить уравнение:

скорость += (скорость — скорость) * 0,3

Чего ждать? Зачем нам скорость?

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

В каждом кадре (тик) мы вычисляем скорость, применяемую к персонажу. На первом кадре скорость равна нулю, поэтому следующее значение скорости будет (0,2–0) * 0,3 = 0,06. В следующем кадре скорость равна 0,06, поэтому мы снова решаем уравнение, используя это как новое значение скорость += (0,2–0,06) * 0,3 = 0,102. Она продолжает увеличиваться до тех пор, пока скорость не станет равной (или примерно равной) скорости (0,2–0,2) * 0,3 = 0. В этот момент скорость остается прежней — персонаж набрал импульс и движется с заданной скоростью. Число смещения определяет, насколько быстро персонаж будет набирать эту скорость.

Наконец, чтобы повернуть персонажа, мы просто применяем вращение к модели.

Результат:

3. Движение камеры

Теперь самое сложное. Мы хотим, чтобы камера следовала за моделью, но мы не хотим, чтобы она была плотно приклеена прямо за спиной персонажа.

Давайте посмотрим на эту схему:

Мы создадим два вспомогательных объекта Follower и Tail. Хвост всегда будет располагаться на фиксированном расстоянии позади персонажа, а Последователь будет плавно перемещаться в положение Хвост. Камера будет добавлена ​​к объекту Follower, чтобы скопировать ее путь.

Просто ради эксперимента я создам видимые 3D-объекты, чтобы продемонстрировать, как это работает.

Как вы заметили, мы также создали четыре вектора. Каждый вектор будет представлять положение каждого объекта, связанного с движением персонажа: положение персонажа, положение последователя, положение хвоста и, наконец, смещение камеры. Зачем столько векторов?

Давайте попробуем просто лерпировать объект Follower (камеру) в хвост:

Последователь выбирает кратчайший путь от lerp до хвоста, а это не совсем то, что нам нужно. Чего мы действительно хотим, так это поддерживать стабильное расстояние от персонажа. В этом отношении мы собираемся ввести вектор camera_offset, чтобы увеличить Vector3 Follower.

Выглядит немного обескураживающе, не так ли?

Однако логика довольно проста, все, что нам нужно сделать, это компенсировать расстояние, увеличив вектор Follower с помощью вектора camera_offset.

Давайте посмотрим на это:

camera_offset.copy(character_position).sub(camera_position).normalize();

  1. Мы берем определенный ранее camera_ofset Vector3, который по умолчанию равен (0,0,0), и копируем позицию символа на каждом тике.
  2. Затем мы вычитаем вектор положения камеры из вектора положения персонажа, как показано на рисунке ниже:

Вычитание векторов: https://www.dummies.com/education/science/physics/how-to-subtract-vectors/#:~:text=To%20subtract%20two%20vectors%2C%20you, you're% 20вычитая%20это%20из.

Затем мы считаем расстояние, которое нам нужно компенсировать:

const DistanceDifference = character_position.distanceTo( camera_position ) — расстояние;

Теперь нам нужно умножить вектор смещения_камеры на distanceDifference и добавить результат к вектору ведомого, чтобы отрегулировать положение ведомого. Для этого мы можем использовать метод addScaledVector из Three.js.

Follower.position.addScaledVector( camera_offset, DistanceDifference );

Наконец-то мы можем дергаться

Follower.position.lerp(tail_position, 0,02);

Читать:

Лерп: https://threejs.org/docs/#api/en/math/Vector3.lerp

Копия: https://threejs.org/docs/#api/en/math/Vector3.copy

Абсолютная позиция: https://threejs.org/docs/#api/en/math/Vector3.setFromMatrixPosition

Результат:

Давайте удалим кубы и оставим вместо них только пустые 3dObjects. И назначьте их Tail и Follower.

пусть хвост = новый THREE.Object3D;

пусть последователь = новый THREE.Object3D;

И добавить камеру к подписчику.

последователь.добавить(камера)

Следующий шаг — анимация!

4. Анимация

Вернемся к простым вещам.

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

  1. Сбросить анимацию для воспроизведения с 0 кадра
  2. Воспроизвести новую анимацию
  3. Применение кроссфейда из предыдущей анимации для плавного перехода

функция crossfadeAnimation (newAction) {

новое действие.сброс()

новое действие.играть()

newAction.crossFadeFrom (действие [предыдущий], 0,3)

}

Чтобы применить кроссфейд, нам нужно где-то сохранить проигранную ранее анимацию. Помните переменную prevAnimam? Это оно.

🎉 Поздравляем, мы закончили!

Полный результат смотрите здесь: https://github.com/oslavdev/webgl- Third-Person-controller

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