mcpField
T
threejs-impl-animation

threejs-impl-animation

github.com/impertio-studio開発23140 回インストール30 週間前に更新
リポジトリ

threejs-impl-animation について

Three.js のアニメーションクリップ、アクション、ミキサーの実装を案内するリファレンスです。再生、ブレンド、時間管理とよくある実装上の注意点を扱います。

主な機能

  1. 01ミキサーとアクションの管理指針
  2. 02クリップ再生とループ設定
  3. 03フェードとクロスフェードのパターン
  4. 04加算ブレンドとモーフターゲットアニメーション
  5. 05Clock によるフレーム時間管理

活用シーン

  1. 01GLTF のキャラクターアニメーション再生
  2. 02アニメーション状態の切り替え
  3. 03呼吸やリアクション動作の重ね合わせ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/impertio-studio/three.js-claude-skill-package --skill threejs-impl-animation

インストール後、Claude Code で/threejs-impl-animationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

.gitkeep
references/
anti-patterns.md
examples.md
methods.md

詳細

threejs-impl-animation

クイックリファレンス

アーキテクチャ
AnimationClip        (data: array of KeyframeTrack objects)
  └── AnimationAction  (playback controller: play, pause, fade, crossfade)
        └── AnimationMixer (master scheduler: one per animated root object)
              └── Clock    (provides delta time for mixer.update)

アニメーション化されたルートオブジェクトごとに、必ず AnimationMixer をちょうど1つ作成してください。 レンダーループ内で、毎フレーム必ず mixer.update(delta) を呼び出してください。 AnimationAction を直接インスタンス化してはいけません。必ず mixer.clipAction(clip) を使用してください。

必須のインポート
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
最小限のアニメーション設定
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const clock = new THREE.Clock();
let mixer;

const loader = new GLTFLoader();
loader.load('character.glb', (gltf) => {
  scene.add(gltf.scene);
  mixer = new THREE.AnimationMixer(gltf.scene);

  // Play all animations from the GLTF file
  gltf.animations.forEach((clip) => {
    mixer.clipAction(clip).play();
  });
});

function animate() {
  const delta = clock.getDelta();
  if (mixer) mixer.update(delta);
  renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
重要な警告

レンダーループ内で mixer.update(delta) を呼び出し忘れてはいけません。呼び出さないとアニメーションは再生されません。

delta を手動で計算するために new Date() や performance.now() を使ってはいけません。安定したフレームタイミングを提供する THREE.Clock または renderer.setAnimationLoop を必ず使用してください。

レンダーループ内で mixer.clipAction(clip) を繰り返し呼び出してはいけません。内部でキャッシュされますが、検索は不要なオーバーヘッドになります。返されたアクションは必ず変数に格納してください。

毎フレーム .play() を呼び出してはいけません。再生を開始するときに1回だけ呼び出してください。すでに再生中のアクションに .play() を再度呼び出しても効果はありませんが、誤解を招く使い方です。

停止または終了したアクションを再開するときは、必ず .play() の前に .reset() を呼び出してください。そうしないと、アクションが最後の位置から再開する場合があります。

LoopOnce を使用する場合は、必ず action.clampWhenFinished = true を設定してください。そうしないと、終了時にアクションが最初のフレームに戻ります。


AnimationMixer

単一のオブジェクト階層に属するすべてのアニメーションアクションを駆動する、マスタースケジューラーです。

コンストラクター
const mixer = new THREE.AnimationMixer(rootObject);

rootObject はアニメーション化されたモデルのルート Object3D です(通常は gltf.scene)。

プロパティ
プロパティ型デフォルト説明
.timenumber0ミキサーのグローバル時間(秒)
.timeScalenumber1グローバル速度倍率。0 にするとすべてのアクションが一時停止します
メソッド
メソッド戻り値説明
.clipAction(clip, root?, blendMode?)AnimationActionクリップのアクションを返すか、新しく作成します
.existingAction(clip, root?)AnimationAction | null以前に作成したアクション、または null を返します
.update(delta)thisdelta 秒分ミキサーを進めます。毎フレーム必ず呼び出してください
.setTime(seconds)thisグローバル時間を設定し、すべてのアクションを更新します
.stopAllAction()thisスケジュールされているすべてのアクションを無効化します
.getRoot()Object3Dミキサーのルートオブジェクトを返します
.uncacheAction(clip, root?)voidキャッシュされたアクションを解放します
.uncacheClip(clip)voidクリップデータを解放します
.uncacheRoot(root)voidルートオブジェクトのデータを解放します
イベント

mixer.addEventListener(type, callback) でリッスンします。

イベント発生するタイミング
'finished'アクションが完了したとき(LoopOnce と clampWhenFinished = true の組み合わせの場合のみ)
'loop'アクションがループを1回完了したとき

イベントオブジェクトのプロパティ:{ action, loopDelta, type }。

ブレンドモード

mixer.clipAction(clip, root, blendMode) の第3引数として渡します。

定数動作
THREE.NormalAnimationBlendMode標準的なブレンド(デフォルト)
THREE.AdditiveAnimationBlendModeベースアニメーションに重ねて適用

AnimationAction

単一のアニメーションクリップの再生を制御します。直接インスタンス化してはいけません。

プロパティ
プロパティ型デフォルト説明
.blendModenumberNormalAnimationBlendModeブレンド方式
.clampWhenFinishedbooleanfalse完了時に最後のフレームで一時停止します
.enabledbooleantrueリセットせずに無効化します
.loopnumberLoopRepeatループモード
.pausedbooleanfalse再生を一時停止します
.repetitionsnumberInfinityループ回数
.timenumber0秒単位のローカル時間
.timeScalenumber1再生速度:0 で一時停止し、負の値で逆再生します
.weightnumber1ブレンドの影響度 [0, 1]
.zeroSlopeAtEndbooleantrueループ終了時の補間を滑らかにします
.zeroSlopeAtStartbooleantrueループ開始時の補間を滑らかにします
ループモード
定数動作
THREE.LoopOnce1 回再生して停止します
THREE.LoopRepeat各ループの開始時に再スタートします
THREE.LoopPingPong順方向と逆方向を交互に再生します
再生メソッド
メソッド説明
.play()再生を開始します
.stop()停止して開始位置にリセットします
.reset()時間、ウェイト、速度を初期状態にリセットします
.startAt(mixerTime)指定したミキサー時間まで開始を遅らせます
フェードとクロスフェードのメソッド
メソッド説明
.fadeIn(duration)ウェイトを 0 から 1 へフェードします
.fadeOut(duration)ウェイトを 1 から 0 へフェードします
.crossFadeFrom(fadeOutAction, duration, warp)別のアクションからこのアクションへクロスフェードします
.crossFadeTo(fadeInAction, duration, warp)このアクションから別のアクションへクロスフェードします
.stopFading()実行中のフェードをキャンセルします
速度とタイミングのメソッド
メソッド説明
.halt(duration)指定した時間をかけて timeScale を 0 まで減速します
.warp(startScale, endScale, duration)再生速度を滑らかに切り替えます
.stopWarping()実行中のワープをキャンセルします
.setDuration(seconds)1 回のループがちょうど seconds 秒になるように timeScale を調整します
.setEffectiveTimeScale(scale)有効なタイムスケールを設定します
.setEffectiveWeight(weight)有効なウェイトを設定します
.setLoop(mode, repetitions)ループモードと回数を設定します
.syncWith(otherAction)別のアクションと時間を同期します
照会メソッド
メソッド戻り値説明
.isRunning()boolean再生中の場合にのみ true を返します
.isScheduled()boolean.play() が呼び出されている場合に true を返します
.getClip()AnimationClip関連付けられたクリップ
.getMixer()AnimationMixer所有するミキサー
.getRoot()Object3Dルートオブジェクト
.getEffectiveTimeScale()number算出されたタイムスケール
.getEffectiveWeight()number算出されたウェイト

AnimationClip

再利用可能なキーフレームトラックのセットです。通常は GLTF ファイルから読み込みます。

コンストラクター
const clip = new THREE.AnimationClip(name, duration, tracks, blendMode);
  • name -- 文字列の識別子(GLTF クリップではファイル内の名前を使用します)
  • duration -- 秒数。トラックから自動計算する場合は -1
  • tracks -- KeyframeTrack オブジェクトの配列
  • blendMode -- オプションのブレンドモード定数
主な静的メソッド
メソッド説明
AnimationClip.findByName(arrayOrObject, name)名前でクリップを検索します
AnimationClip.CreateFromMorphTargetSequence(name, targets, fps, noLoop)モーフターゲットからクリップを作成します
AnimationClip.parse(json)JSON からデシリアライズします

KeyframeTrack の型

トラックの型値の型用途
VectorKeyframeTrackVector3位置、スケール
QuaternionKeyframeTrackQuaternion回転(slerp を使用)
NumberKeyframeTracknumber不透明度、強度
BooleanKeyframeTrackboolean表示状態の切り替え
ColorKeyframeTrackColor色のアニメーション
StringKeyframeTrackstring離散的な文字列値
補間モード
定数動作
THREE.InterpolateDiscreteステップ関数。スムージングなし
THREE.InterpolateLinear線形補間(デフォルト)
THREE.InterpolateSmooth3 次スプライン補間
PropertyBinding のパス形式
"meshName.position"                    // animate position
"meshName.material.opacity"            // animate material property
"meshName.morphTargetInfluences[0]"    // animate morph target
"boneName.quaternion"                  // animate bone rotation

Clock

コンストラクター
const clock = new THREE.Clock(autoStart); // autoStart defaults to true
メソッド
メソッド戻り値説明
.getDelta()number前回の getDelta() 呼び出しからの経過秒数
.getElapsedTime()number経過時間の合計(秒)
.start()voidクロックを開始します
.stop()voidリセットせずに停止します

クロスフェードのパターン(キャラクターのステートマシン)

const actions = {};
gltf.animations.forEach((clip) => {
  actions[clip.name] = mixer.clipAction(clip);
});

let currentAction = actions['Idle'];
currentAction.play();

function switchAction(toName, duration = 0.5) {
  const toAction = actions[toName];
  toAction.reset();
  toAction.setEffectiveTimeScale(1);
  toAction.setEffectiveWeight(1);
  toAction.crossFadeFrom(currentAction, duration, true);
  toAction.play();
  currentAction = toAction;
}

クロスフェードの前に、必ず新しいアクションに .reset() を呼び出してください。 次の遷移に備えて、必ず現在のアクションへの参照を保存してください。


加算アニメーションブレンディング

const baseAction = mixer.clipAction(baseClip);
const additiveAction = mixer.clipAction(
  additiveClip, undefined, THREE.AdditiveAnimationBlendMode
);

baseAction.play();
additiveAction.play();
additiveAction.setEffectiveWeight(0.5);

呼吸、ダメージリアクション、照準オフセットなどのレイヤー化エフェクトには、加算ブレンディングを使用してください。


モーフターゲットアニメーション

// Manual control
mesh.morphTargetInfluences[0] = Math.sin(elapsed) * 0.5 + 0.5;

// Via GLTF animation clip (preferred)
const morphAction = mixer.clipAction(morphClip);
morphAction.play();

参考リンク

公式ソース

コメント