Механизм анимации в Three.js основан на объекте
AnimationMixer, который управляет воспроизведением
анимационных клипов (AnimationClip) через экземпляры
AnimationAction. Каждый клип содержит набор ключевых
кадров, а AnimationAction отвечает за конкретное
проигрывание этого клипа: запуск, остановку, изменение скорости,
зацикливание и смешивание с другими действиями.
При работе со сложными сценами часто требуется плавный переход между анимациями. Например:
Мгновенная смена анимации создаёт резкий визуальный скачок, поскольку положение костей модели изменяется без интерполяции. Для устранения этого эффекта используется смешивание анимаций (animation blending).
Основными инструментами для такого перехода являются:
crossFadeTo()weightОни позволяют постепенно уменьшать влияние одной анимации и увеличивать влияние другой.
Каждый объект AnimationAction имеет параметр веса (weight). Этот параметр определяет, насколько сильно анимация влияет на итоговую позу объекта.
Диапазон значений:
0.0 — анимация не влияет на модель
1.0 — анимация полностью влияет на модель
При наличии нескольких одновременно воспроизводимых анимаций их трансформации смешиваются пропорционально весам.
Пример:
| Анимация | Weight |
|---|---|
| Idle | 0.7 |
| Walk | 0.3 |
Итоговая поза будет комбинацией двух анимаций, где 70% позы формируется анимацией ожидания и 30% — ходьбой.
Перед использованием смешивания необходимо создать миксер анимации.
const mixer = new THREE.AnimationMixer(model);
Затем из анимационных клипов создаются действия:
const idleAction = mixer.clipAction(idleClip);
const walkAction = mixer.clipAction(walkClip);
const runAction = mixer.clipAction(runClip);
Каждый AnimationAction может воспроизводиться
независимо:
idleAction.play();
Однако в реальных сценах одновременно могут работать несколько действий с разными весами.
Вес можно устанавливать напрямую.
walkAction.setEffectiveWeight(1.0);
idleAction.setEffectiveWeight(0.0);
Также существует свойство:
action.weight
Однако предпочтительнее использовать методы:
setEffectiveWeight()
getEffectiveWeight()
Они учитывают дополнительные модификаторы (например, fade).
idleAction.setEffectiveWeight(1);
walkAction.setEffectiveWeight(0);
После этого модель полностью находится в состоянии ожидания.
Постепенное изменение:
walkAction.setEffectiveWeight(0.5);
idleAction.setEffectiveWeight(0.5);
Теперь итоговая поза является смешанной.
Ручное изменение веса требует постоянного обновления значений, что
неудобно. Для автоматизации используется система
fading, реализованная внутри
AnimationAction.
Существует несколько методов:
fadeIn()
fadeOut()
crossFadeTo()
crossFadeFrom()
Наиболее распространённый вариант — crossFadeTo.
Метод crossFadeTo() выполняет
перекрёстное затухание (cross-fade) между двумя
анимациями.
Сигнатура метода:
action.crossFadeTo(targetAction, duration, warp)
Параметры:
| Параметр | Описание |
|---|---|
| targetAction | анимация, к которой выполняется переход |
| duration | длительность перехода (в секундах) |
| warp | синхронизация скорости анимаций |
Во время перехода:
idleAction.play();
walkAction.play();
idleAction.crossFadeTo(walkAction, 0.5, false);
Процесс:
idleAction.weight уменьшаетсяwalkAction.weight увеличиваетсяИтог — плавная смена состояния персонажа.
function fadeToAction(action, duration) {
previousAction = activeAction;
activeAction = action;
if (previousAction !== activeAction) {
previousAction.fadeOut(duration);
}
activeAction
.reset()
.setEffectiveTimeScale(1)
.setEffectiveWeight(1)
.fadeIn(duration)
.play();
}
Использование:
fadeToAction(walkAction, 0.5);
fadeToAction(runAction, 0.5);
Такой подход используется во многих демо Three.js.
Метод crossFadeFrom() работает
аналогично, но вызывается на целевой анимации.
runAction.crossFadeFrom(walkAction, 0.5, false);
Внутренне происходит:
walk → fadeOut
run → fadeIn
Оба метода приводят к одинаковому результату.
Параметр warp используется для синхронизации времени анимаций.
crossFadeTo(targetAction, duration, true)
Если длина клипов различается, система автоматически изменит timeScale, чтобы фазы движений совпали.
Пример:
| Анимация | Длительность |
|---|---|
| Walk | 1.2 сек |
| Run | 0.7 сек |
При включённом warp система скорректирует скорость
проигрывания.
Это особенно важно для:
Three.js поддерживает слоистую систему анимаций.
Например:
| Анимация | Назначение |
|---|---|
| Walk | движение |
| Shoot | стрельба |
| LookAround | поворот головы |
Каждая из них может иметь собственный weight.
Пример:
Walk weight = 1
Shoot weight = 0.4
LookAround weight = 0.2
В результате:
Такой подход широко применяется в игровых движках.
Для работы системы анимации необходимо обновлять миксер каждый кадр.
const delta = clock.getDelta();
mixer.update(delta);
Метод update():
Без вызова update() анимации не будут
воспроизводиться.
В проектах обычно используется контроллер состояний персонажа.
Пример структуры:
Idle
Walk
Run
Jump
Attack
Каждое состояние соответствует отдельному
AnimationAction.
Переключение:
Idle → Walk
Walk → Run
Run → Jump
Jump → Idle
Переход выполняется через crossFadeTo.
При работе с большим количеством персонажей важно учитывать производительность.
Основные рекомендации:
Не следует одновременно воспроизводить десятки действий.
Обычно достаточно:
2–3 активных action
Перед повторным запуском анимации полезно сбросить её состояние.
action.reset();
Это возвращает анимацию к первому кадру.
Если анимация больше не используется:
action.stop();
Ошибка:
walkAction.stop();
runAction.play();
Результат — скачок позы.
Правильный вариант:
walkAction.crossFadeTo(runAction, 0.3, false);
Иногда забывают вызвать play().
Неправильно:
idleAction.crossFadeTo(walkAction, 0.5);
Правильно:
walkAction.play();
idleAction.crossFadeTo(walkAction, 0.5);
Без обновления миксера:
crossFade не работает
анимации не воспроизводятся
Необходим код в цикле рендера:
mixer.update(clock.getDelta());
let activeAction;
let previousAction;
function setAction(newAction) {
previousAction = activeAction;
activeAction = newAction;
if (previousAction !== activeAction) {
previousAction.fadeOut(0.5);
activeAction
.reset()
.setEffectiveWeight(1)
.fadeIn(0.5)
.play();
}
}
Использование:
setAction(idleAction);
setAction(walkAction);
setAction(runAction);
Каждый вызов создаёт плавный переход.
Метод crossFadeTo() является ключевым инструментом при
построении:
Он обеспечивает:
В сочетании с управлением весами (weight) система
анимации Three.js позволяет реализовывать сложные анимационные
графы, сопоставимые с возможностями игровых движков.