AnimationMixer: воспроизведение и управление

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

В архитектуре анимации Three.js существует несколько ключевых компонентов:

  • AnimationClip — набор анимационных данных (ключевые кадры).
  • KeyframeTrack — последовательность значений для конкретного свойства.
  • AnimationAction — объект управления воспроизведением конкретного клипа.
  • AnimationMixer — система, обновляющая и комбинирующая действия (AnimationAction).

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


Создание AnimationMixer

Экземпляр миксера создаётся с передачей корневого объекта:

const mixer = new THREE.AnimationMixer(object);

object — объект сцены (Object3D), на который будут применяться анимации. Чаще всего используется:

  • загруженная 3D-модель,
  • группа (THREE.Group),
  • скелетная модель (SkinnedMesh).

Пример после загрузки модели формата GLTF:

const loader = new GLTFLoader();

loader.load('model.glb', function(gltf) {

    const model = gltf.scene;
    scene.add(model);

    const mixer = new THREE.AnimationMixer(model);

    const action = mixer.clipAction(gltf.animations[0]);
    action.play();

});

gltf.animations содержит массив клипов (AnimationClip), которые можно передать миксеру.


Обновление анимаций

AnimationMixer не обновляется автоматически. Для корректной работы требуется вызов метода update() каждый кадр.

mixer.update(deltaTime);

deltaTime — время, прошедшее с предыдущего кадра, обычно вычисляется через Clock.

const clock = new THREE.Clock();

function animate() {

    requestAnimationFrame(animate);

    const delta = clock.getDelta();
    mixer.update(delta);

    renderer.render(scene, camera);

}

Такой подход обеспечивает независимость скорости анимации от частоты кадров.


Создание AnimationAction

Метод clipAction() создаёт объект управления анимацией.

const action = mixer.clipAction(animationClip);

Этот объект представляет конкретное воспроизведение клипа, включая параметры:

  • скорость
  • цикл
  • вес смешивания
  • состояние воспроизведения

Если для одного и того же клипа вызвать clipAction() повторно, будет возвращён уже существующий AnimationAction.


Воспроизведение анимации

Запуск анимации выполняется методом play():

action.play();

Остановка:

action.stop();

Пауза:

action.paused = true;

Возобновление:

action.paused = false;

Управление временем

Каждое действие анимации имеет собственное время воспроизведения.

action.time = 0;

Скорость анимации изменяется через timeScale.

action.timeScale = 2;

Значения:

  • 1 — нормальная скорость
  • 2 — ускорение в 2 раза
  • 0.5 — замедление
  • отрицательные значения — воспроизведение в обратном направлении

Типы циклов анимации

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

action.setLoop(mode, repetitions);

Доступные режимы:

THREE.LoopOnce
THREE.LoopRepeat
THREE.LoopPingPong

Примеры:

Однократное воспроизведение:

action.setLoop(THREE.LoopOnce);

Повтор 5 раз:

action.setLoop(THREE.LoopRepeat, 5);

Эффект «туда-обратно»:

action.setLoop(THREE.LoopPingPong);

Для одноразовой анимации часто используется:

action.clampWhenFinished = true;

Это фиксирует объект в последнем кадре после завершения.


Смешивание анимаций

AnimationMixer поддерживает blending — плавное смешивание нескольких анимаций.

Каждое действие имеет параметр веса (weight).

0 — анимация не влияет  
1 — полное влияние

Пример:

walkAction.weight = 0.7;
runAction.weight = 0.3;

Это позволяет комбинировать движения, например:

  • ходьба + поворот головы
  • бег + жест рукой

Плавные переходы между анимациями

Наиболее распространённая задача — плавное переключение между клипами.

Для этого используется crossFade.

action1.crossFadeTo(action2, 0.5, true);

Параметры:

action2 — новая анимация
0.5 — длительность перехода (секунды)
true — синхронизация времени

Пример:

idleAction.crossFadeTo(walkAction, 0.3, true);
walkAction.play();

В течение 0.3 секунды происходит постепенное уменьшение веса первой анимации и увеличение веса второй.

Альтернативный вариант:

walkAction.crossFadeFrom(idleAction, 0.3);

Плавное изменение веса

Иногда требуется постепенно усилить или ослабить влияние анимации.

action.fadeIn(1);

или

action.fadeOut(1);

Значение указывает продолжительность в секундах.

Пример:

walkAction.fadeIn(0.5);
idleAction.fadeOut(0.5);

Управление всеми действиями

AnimationMixer хранит список всех действий, созданных через clipAction.

Полная остановка:

mixer.stopAllAction();

Удаление кэша действий:

mixer.uncacheClip(clip);

или

mixer.uncacheRoot(object);

Эти методы освобождают память, когда анимации больше не используются.


События анимации

AnimationMixer генерирует события окончания клипа.

Обработчик:

mixer.addEventListener('finished', function(e) {

    console.log('Animation finished', e.action);

});

Также существует событие:

loop

Пример:

mixer.addEventListener('loop', function(e) {

    console.log('Animation looped');

});

Это удобно для реализации игровых состояний.


Управление глобальным временем

AnimationMixer имеет собственное внутреннее время.

mixer.time

Скорость всего миксера:

mixer.timeScale = 0.5;

Это замедляет все анимации одновременно.

Применяется для эффектов:

  • замедление времени
  • пауза сцены
  • ускорение игровых событий

Использование нескольких миксеров

В сложных сценах возможно создание нескольких миксеров.

Пример:

один миксер — персонаж  
второй — транспорт  
третий — механизмы окружения

Каждый обновляется отдельно:

mixerCharacter.update(delta);
mixerVehicle.update(delta);
mixerEnvironment.update(delta);

Это обеспечивает независимое управление различными системами анимации.


Практический пример управления состояниями персонажа

const mixer = new THREE.AnimationMixer(character);

const idle = mixer.clipAction(clips.idle);
const walk = mixer.clipAction(clips.walk);
const run = mixer.clipAction(clips.run);

idle.play();

function setState(action) {

    const previous = activeAction;
    activeAction = action;

    previous.fadeOut(0.3);
    activeAction
        .reset()
        .fadeIn(0.3)
        .play();

}

Переключение состояний:

idle → walk → run

Вызов:

setState(walk);
setState(run);

Между анимациями происходит плавный переход.


Оптимизация производительности

Несколько правил эффективного использования AnimationMixer:

1. Один миксер на модель

Создание большого количества миксеров для одной модели увеличивает нагрузку.

2. Использование clipAction повторно

Метод кэширует действия, поэтому повторный вызов безопасен.

3. Остановка неиспользуемых анимаций

action.stop();

4. Очистка ресурсов

mixer.uncacheRoot(object);

Это особенно важно при удалении моделей из сцены.


Структура системы анимации Three.js

Связь компонентов выглядит следующим образом:

AnimationMixer
      │
      ├── AnimationAction
      │        │
      │        └── AnimationClip
      │                 │
      │                 └── KeyframeTrack
      │
      └── Object3D (модель)

AnimationMixer управляет действиями (AnimationAction), которые воспроизводят клипы (AnimationClip), содержащие ключевые кадры (KeyframeTrack). Результаты анимации применяются к объектам сцены (Object3D).

Такая архитектура позволяет:

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