threejs モデルに付属したアニメーション

モデルに付属したアニメーションを制御する方法を纏めてみます

mixer action制作

actions = [];

this.loader = new GLTFLoader();

loader.load("RobotExpressive.glb", (gltf) => {
      scene.add(gltf.scene);
      
      mixer = new THREE.AnimationMixer(gltf.scene)
      for(const clip of gltf.animations){
        const action = mixer.clipAction(clip)
        actions.push(action)
      }
    });

●ローダーを使ってモデルをロードします。今回はGLTFLoaderを使います
●gltfにモデル全体のデータであるsceneアニメーションデータのanimationsが入っている
●scene.add(gltf.scene)・・・モデル全体をThree.jsに追加し、レンダリングするようにしている
●mixer = new THREE.AnimationMixer(gltf.scene)・・・モデル全体のデータを渡し、モデルがアニメーションを正しく適用できるようにしたmixerを作ります。
●const action = mixer.clipAction(clip)によって1つのアニメーションを再生・停止など制御できるようになります。

actionは一つのanimationに対して一つです。例えば「歩く」「走る」などの動きがあったとき、actionはそれぞれに作成しなければなりません。

再生

actions[0].play();
const clock = new THREE.Clock();
const deltaTime = clock.getDelta();
//renderアニメーションループの中で
    mixer.update(deltaTime);

再生のときはアニメーションループの中でmixer.update()をします。そこに渡すのはthree Clockの経過時間。またmixerやactionが作成できる前に実行されるとエラーが出るので、if文で存在を明確にするか非同期関数にするなり工夫する必要があります。

アニメーション遷移

複数のアニメーションをなめらかに切り替える

prevAnimation = null;
currentAnimation = null;

playAnimation(index, emote) {
    prevAnimation = currentAnimation;
    currentAnimation = actions[index];

    if (prevAnimation !== currentAnimation && prevAnimation) {
      this.prevAnimation.fadeOut(0.5);//0.5秒かけてフェードアウト
    }

  //倒れる、などループせず一回で終わりそのままのポーズで固定される場合
    if (emote) {
      currentAnimation.clampWhenFinished = true;//終了フレームで固定
      currentAnimation.loop = THREE.LoopOnce;//ループ一回で終了
    }
    currentAnimation.reset();
    currentAnimation.fadeIn(0.5);//0.5秒かけてフェードイン
    currentAnimation.play();
  }

//return文の中で
<button onClick={() => playAnimation(0, false)}>Dancing</button>
<button onClick={() => playAnimation(1, true)}>Death</button>
//他の調整用コード
.setEffectiveTimeScale(1)//アニメーション測度、デフォルト1、0.5で半分の速度
.setEffectiveWeight(1)//重みづけ、デフォルト1で完全な影響

●前、現のアニメーションを保存する
●emoteという引数(boolean)で、ループするアクションなのかどうかを判定,一回で終わるアクションの場合は終了フレームで固定するように設定
●動作開始時にはreset()をいれる。(一回停止したアクションはリセットしないとずっと停止したままで動かない)

メモリの削除

3dモデル、オブジェクトのデータはブラウザのメモリに残り、パフォーマンスを落としてしまうので、ページのアンマウント時にデータをクリアする必要があります。

  useEffect(() => {
    three.initScene();
    return () => {
      three.cleanUp();
    };
  }, []);