Живая демонстрация: https://webgl- Third-Person-controller.vercel.app/
Управление
- W — двигаться вперед
- S — двигаться назад
- A — поверните налево
- D — поверните направо
- Shift + W — Бежать вперед 🚀
Для прохождения этого пошагового руководства необходимы базовые знания javascript и Three.js. Это не учебник по Three.js, я сосредоточусь на самой механике управления персонажем.
Официальная документация Three.js действительно хороша и содержит множество полезных примеров, которые я настоятельно рекомендую прочитать.
- Подготовка
Прежде всего, мы загрузим модель и добавим ее в сцену, а затем настроим перспективную камеру на смотреть персонажа. Также нам нужно извлечь анимацию и назначить ее объекту 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();
- Мы берем определенный ранее camera_ofset Vector3, который по умолчанию равен (0,0,0), и копируем позицию символа на каждом тике.
- Затем мы вычитаем вектор положения камеры из вектора положения персонажа, как показано на рисунке ниже:
Вычитание векторов: 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. Анимация
Вернемся к простым вещам.
Мы хотим иметь плавный переход между анимациями. Для этого мы создадим функцию анимации плавного перехода, в которой мы будем переключаться между анимациями в три шага:
- Сбросить анимацию для воспроизведения с 0 кадра
- Воспроизвести новую анимацию
- Применение кроссфейда из предыдущей анимации для плавного перехода
функция crossfadeAnimation (newAction) {
новое действие.сброс()
новое действие.играть()
newAction.crossFadeFrom (действие [предыдущий], 0,3)
}
Чтобы применить кроссфейд, нам нужно где-то сохранить проигранную ранее анимацию. Помните переменную prevAnimam? Это оно.
🎉 Поздравляем, мы закончили!
Полный результат смотрите здесь: https://github.com/oslavdev/webgl- Third-Person-controller
В следующих статьях мы добавим прелоадер, контроллер тачпада и физику.