# 機能設計書 110-AnimationMixer

## 概要

本ドキュメントは、Three.jsライブラリにおけるアニメーション再生管理機能「AnimationMixer」の設計について記述する。AnimationMixerは、特定のObject3Dに対するアニメーション再生を統括し、複数のAnimationClipの同時再生、ブレンディング、時間制御を行うプレイヤークラスである。

### 本機能の処理概要

**業務上の目的・背景**：3Dアニメーションにおいて、キャラクターやオブジェクトは複数のアニメーション（歩行、走行、ジャンプ等）を持ち、状況に応じて切り替えやブレンドが必要となる。AnimationMixerは、これらのアニメーションを効率的に管理・再生するための中核コンポーネントであり、スケルタルアニメーション、モーフターゲット、プロパティアニメーションなど様々なアニメーション形式をサポートする。

**機能の利用シーン**：
- キャラクターアニメーションの再生
- カメラアニメーション
- オブジェクトのプロパティアニメーション
- アニメーションの遷移（クロスフェード）
- 複数アニメーションの同時再生とブレンド

**主要な処理内容**：
1. clipAction(): AnimationClipからAnimationActionを生成
2. update(): フレームごとの時間更新とアニメーション進行
3. アクション管理（アクティブ/非アクティブの切り替え）
4. プロパティバインディングの管理
5. イベント発行（loop, finished）

**関連システム・外部連携**：
- AnimationClip: 再生するアニメーションデータ
- AnimationAction: 個々のアニメーション再生を制御
- PropertyBinding: オブジェクトプロパティへのバインディング
- PropertyMixer: プロパティ値のミキシング
- Clock/Timer: デルタタイムの取得

**権限による制御**：特になし

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 直接関連する画面なし（プログラムからの利用が主） |

## 機能種別

アニメーション / 再生管理 / イベントディスパッチ

## 入力仕様

### コンストラクタ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| root | Object3D | Yes | アニメーション対象のルートオブジェクト | なし |

### 主要プロパティ

| プロパティ名 | 型 | デフォルト | 説明 |
|-------------|-----|----------|------|
| time | number | 0 | グローバルミキサー時間（秒） |
| timeScale | number | 1.0 | 時間のスケーリング係数 |

### 主要メソッド

| メソッド名 | 引数 | 戻り値 | 説明 |
|-----------|------|--------|------|
| clipAction | (clip, optionalRoot?, blendMode?) | AnimationAction | クリップからアクションを取得/生成 |
| existingAction | (clip, optionalRoot?) | AnimationAction\|null | 既存のアクションを取得 |
| update | (deltaTime: number) | AnimationMixer | フレーム更新 |
| setTime | (time: number) | AnimationMixer | 特定時刻にジャンプ |
| stopAllAction | () | AnimationMixer | 全アクション停止 |
| uncacheClip | (clip) | void | クリップのキャッシュ解放 |
| uncacheRoot | (root) | void | ルートのキャッシュ解放 |
| uncacheAction | (clip, optionalRoot?) | void | アクションのキャッシュ解放 |

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| action | AnimationAction | clipAction()で返されるアクション |

### イベント

| イベント名 | ペイロード | 発生条件 |
|-----------|-----------|---------|
| loop | { action, loopDelta } | ループ境界を通過時 |
| finished | { action, direction } | アニメーション終了時 |

## 処理フロー

### 処理シーケンス（update）

```
1. update(deltaTime)呼び出し
2. deltaTime *= this.timeScale
3. this.time += deltaTime
4. アクティブアクションのループ処理
   └─ action._update(time, deltaTime, timeDirection, accuIndex)
5. アクティブバインディングの適用
   └─ binding.apply(accuIndex)
```

### フローチャート

```mermaid
flowchart TD
    A[update開始] --> B[deltaTime *= timeScale]
    B --> C[time += deltaTime]
    C --> D[accuIndex切り替え]
    D --> E[for each activeAction]
    E --> F[action._update]
    F --> G{全アクション処理完了?}
    G -->|No| E
    G -->|Yes| H[for each activeBinding]
    H --> I[binding.apply]
    I --> J{全バインディング処理完了?}
    J -->|No| H
    J -->|Yes| K[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-110-1 | アクション共有 | 同じclip/rootの組み合わせは同じアクションを返す | clipAction()呼び出し時 |
| BR-110-2 | ダブルバッファリング | accuIndexを交互に切り替えてプロパティ値を蓄積 | update()時 |
| BR-110-3 | 遅延バインド | アクションは再生開始時にバインドされる | play()呼び出し時 |
| BR-110-4 | 参照カウント | バインディングは参照カウントで管理 | アクション有効化/無効化時 |

### 計算ロジック

- 時間更新: `this.time += deltaTime * this.timeScale`
- 時間方向: `timeDirection = Math.sign(deltaTime)`
- アキュムレータインデックス: `accuIndex = this._accuIndex ^= 1`

## データベース操作仕様

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | クリップ未発見 | 文字列でクリップ指定時に見つからない | null返却 |
| - | バインディング失敗 | プロパティパス解決失敗 | warn出力、処理継続 |

## トランザクション仕様

該当なし

## パフォーマンス要件

- メモリマネージャーによる効率的なアクション/バインディング管理
- アクティブ/非アクティブの分離によるupdate()の最適化
- lend/takeBack パターンによるオブジェクトプーリング

## セキュリティ考慮事項

特になし

## 備考

- AnimationMixerはEventDispatcherを継承しており、イベント購読が可能
- timeScale = 0でアニメーション一時停止
- 負のtimeScaleで逆再生
- 複数のミキサーが同じオブジェクト階層を制御する場合は競合に注意

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: 関連クラスの構造を理解する

AnimationMixerが使用する主要クラスの関係を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AnimationClip.js | `src/animation/AnimationClip.js` | name, duration, tracks, blendMode |
| 1-2 | AnimationAction.js | `src/animation/AnimationAction.js` | play, stop, reset, loop設定 |
| 1-3 | PropertyBinding.js | `src/animation/PropertyBinding.js` | パス解析、ノード検索 |

**読解のコツ**: AnimationMixer → AnimationAction → PropertyBinding/PropertyMixer の階層構造を理解する。

#### Step 2: メモリマネージャーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AnimationMixer.js | `src/animation/AnimationMixer.js` | 199-264行目: _initMemoryManager() |

**主要処理フロー**:
- **201-209行目**: アクションの管理構造（_actions, _nActiveActions, _actionsByClip）
- **212-215行目**: バインディングの管理構造（_bindings, _nActiveBindings）
- **218-219行目**: コントロール補間の管理
- **223-262行目**: statsプロパティ（デバッグ用統計情報）

#### Step 3: アクション管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AnimationMixer.js | `src/animation/AnimationMixer.js` | 557-616行目: clipAction() |

**主要処理フロー**:
- **562-564行目**: クリップの文字列/オブジェクト解決
- **566行目**: 既存アクションの検索
- **583-600行目**: 既存アクション返却または新規作成
- **607行目**: new AnimationAction()
- **609行目**: _bindAction()でバインド
- **612行目**: _addInactiveAction()でキャッシュ

#### Step 4: update処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AnimationMixer.js | `src/animation/AnimationMixer.js` | 676-711行目: update() |

**主要処理フロー**:
- **678行目**: deltaTime *= this.timeScale
- **683行目**: time更新
- **684行目**: timeDirection計算
- **686行目**: accuIndex切り替え（XOR）
- **690-696行目**: アクティブアクションのループ更新
- **700-707行目**: アクティブバインディングの適用

### プログラム呼び出し階層図

```
AnimationMixer.clipAction(clip, optionalRoot, blendMode)
    │
    ├─ AnimationClip.findByName() [文字列クリップ解決]
    │
    ├─ _actionsByClip[clipUuid] [既存アクション検索]
    │
    ├─ new AnimationAction(this, clipObject, optionalRoot, blendMode)
    │
    ├─ _bindAction(newAction, prototypeAction)
    │      │
    │      └─ for each track
    │             ├─ PropertyBinding.create()
    │             └─ new PropertyMixer()
    │
    └─ _addInactiveAction(newAction, clipUuid, rootUuid)

AnimationMixer.update(deltaTime)
    │
    ├─ deltaTime *= timeScale
    │
    ├─ time += deltaTime
    │
    ├─ for i = 0 to nActiveActions
    │      └─ actions[i]._update(time, deltaTime, timeDirection, accuIndex)
    │             ├─ _updateWeight(time)
    │             ├─ _updateTimeScale(time)
    │             ├─ _updateTime(deltaTime)
    │             └─ interpolants[j].evaluate(clipTime)
    │                    └─ propertyMixers[j].accumulate(accuIndex, weight)
    │
    └─ for i = 0 to nActiveBindings
           └─ bindings[i].apply(accuIndex)
```

### データフロー図

```
[入力]                    [処理]                           [出力]

AnimationClip ──▶ clipAction() ──▶ AnimationAction
                       │
                       ▼
                   _bindAction()
                       │
       ┌───────────────┼───────────────┐
       │               │               │
       ▼               ▼               ▼
PropertyBinding  PropertyMixer   Interpolant


deltaTime ──▶ update() ──▶ 更新されたオブジェクトプロパティ
                  │
                  ├── アクティブアクション更新
                  │       ├── 時間計算
                  │       ├── 補間評価
                  │       └── 値の蓄積
                  │
                  └── バインディング適用
                          └── プロパティ値設定
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AnimationMixer.js | `src/animation/AnimationMixer.js` | ソース | ミキサー本体（861行） |
| AnimationAction.js | `src/animation/AnimationAction.js` | ソース | アクション制御（933行） |
| AnimationClip.js | `src/animation/AnimationClip.js` | ソース | クリップデータ（629行） |
| PropertyBinding.js | `src/animation/PropertyBinding.js` | ソース | プロパティバインディング（795行） |
| PropertyMixer.js | `src/animation/PropertyMixer.js` | ソース | 値のミキシング |
| KeyframeTrack.js | `src/animation/KeyframeTrack.js` | ソース | キーフレームデータ |
| EventDispatcher.js | `src/core/EventDispatcher.js` | ソース | イベント基底クラス |
