# 機能設計書 112-AnimationAction

## 概要

本ドキュメントは、Three.jsにおける`AnimationAction`クラスの機能設計を記述する。AnimationActionは、AnimationClipの再生を制御するクラスであり、再生・停止・一時停止・フェード・ワープなどのアニメーション再生制御機能を提供する。

### 本機能の処理概要

AnimationActionは、AnimationMixerとAnimationClipを橋渡しし、アニメーションの具体的な再生制御を行うコントローラークラスである。再生状態の管理、ループ設定、ブレンディング、時間スケール調整など、アニメーション再生に必要なすべての制御機能を提供する。

**業務上の目的・背景**：3Dコンテンツにおいて、単にアニメーションを再生するだけでなく、ゲームやインタラクティブコンテンツでは、ユーザー操作に応じたアニメーションの切り替え、ブレンド、速度調整などが必要となる。AnimationActionはこれらの高度な再生制御を実現し、スムーズなアニメーション遷移やリアルタイムな再生制御を可能にする。

**機能の利用シーン**：
- キャラクターの歩行から走行へのスムーズな遷移（クロスフェード）
- アニメーション速度のリアルタイム調整（スローモーション、早送り）
- アニメーションのループ再生設定
- 複数アニメーションの同時再生とブレンド
- アニメーションの特定時点からの再生

**主要な処理内容**：
1. アニメーションの再生開始（play）、停止（stop）、一時停止（pause）
2. ループモードの設定（LoopRepeat, LoopOnce, LoopPingPong）
3. 重み（weight）による複数アニメーションのブレンド
4. フェードイン・フェードアウトによる滑らかな遷移
5. ワープ機能による再生速度の動的変更
6. クロスフェードによる2つのアクション間の遷移

**関連システム・外部連携**：AnimationMixerから生成され、AnimationClipとPropertyMixerを介してオブジェクトのプロパティを更新する。イベントディスパッチャー機構により、ループ完了やアニメーション終了を通知する。

**権限による制御**：特になし（ライブラリ内部機能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 該当なし | - | - | AnimationActionはライブラリ内部で使用されるクラスであり、直接的なUI関連はない |

## 機能種別

アニメーション再生制御 / 状態管理 / イベント発火

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| mixer | AnimationMixer | Yes | このアクションを制御するミキサー | - |
| clip | AnimationClip | Yes | 再生するアニメーションクリップ | - |
| localRoot | Object3D | No | アクションを実行するルートオブジェクト | デフォルト: null（ミキサーのルートを使用） |
| blendMode | number | No | ブレンドモード | デフォルト: clip.blendMode |

### 入力データソース

- AnimationMixer.clipAction()からのインスタンス生成
- AnimationClipからのアニメーションデータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| loop | number | ループモード（LoopRepeat/LoopOnce/LoopPingPong） |
| time | number | 現在の再生時間（秒） |
| timeScale | number | 再生速度スケール |
| weight | number | ブレンド重み |
| repetitions | number | ループ回数 |
| paused | boolean | 一時停止フラグ |
| enabled | boolean | 有効フラグ |
| clampWhenFinished | boolean | 終了時にクランプするか |

### 出力先

- PropertyMixerを介してオブジェクトプロパティに反映
- AnimationMixerへのイベント通知（finished, loop）

## 処理フロー

### 処理シーケンス

```
1. play() - 再生開始
   └─ mixer._activateAction(this)を呼び出し
   └─ アクションをアクティブ状態に

2. stop() - 停止
   └─ mixer._deactivateAction(this)を呼び出し
   └─ reset()で状態をリセット

3. pause() - 一時停止
   └─ _progressを更新して現在位置を記録
   └─ source.stop()で再生停止
   └─ isPlaying = false

4. _update() - フレーム毎の更新（ミキサーから呼び出し）
   └─ 開始時間のチェック
   └─ タイムスケールの更新
   └─ クリップ時間の更新
   └─ 重みの更新
   └─ インターポラントの評価
   └─ プロパティミキサーへの蓄積
```

### フローチャート

```mermaid
flowchart TD
    A[play()呼び出し] --> B[mixer._activateAction]
    B --> C{毎フレーム _update}
    C --> D{enabled?}
    D -->|No| E[_updateWeightのみ]
    D -->|Yes| F{startTime到達?}
    F -->|No| G[deltaTime = 0]
    F -->|Yes| H[startTime = null]
    G --> I[タイムスケール更新]
    H --> I
    I --> J[クリップ時間更新]
    J --> K[重み更新]
    K --> L{weight > 0?}
    L -->|Yes| M[インターポラント評価]
    M --> N[PropertyMixer蓄積]
    L -->|No| O[スキップ]
    N --> P{ループ終了?}
    P -->|Yes| Q[finishedイベント発火]
    P -->|No| C
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-112-01 | デフォルトループ | loopはLoopRepeatがデフォルト | コンストラクタ実行時 |
| BR-112-02 | 有効重み計算 | enabled=falseの場合、effectiveWeightは0 | _updateWeight実行時 |
| BR-112-03 | クランプ動作 | clampWhenFinished=trueの場合、終了時にpausedになる | LoopOnce終了時 |
| BR-112-04 | PingPongモード | LoopPingPong時は奇数ループで時間が反転 | _updateTime実行時 |

### 計算ロジック

**有効タイムスケール計算**:
```javascript
effectiveTimeScale = paused ? 0 : timeScale * interpolant値
```

**有効重み計算**:
```javascript
effectiveWeight = enabled ? weight * interpolant値 : 0
```

**デュレーション設定**:
```javascript
timeScale = clip.duration / duration
```

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

該当なし（メモリ内データ構造）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 警告 | 既に再生中のアクションでplay()を呼び出し | 呼び出し前にisRunning()でチェック |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- weight=0のアクションはインターポラント評価をスキップ
- enabled=falseのアクションは軽量な重み更新のみ実行

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

特になし

## 備考

- AnimationActionはAnimationMixer.clipAction()で取得し、直接コンストラクタを呼ぶことは推奨されない
- 複数のアクションを同時に再生する場合、重みの合計が1になるように調整する

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

AnimationActionのプロパティと初期状態を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AnimationAction.js | `src/animation/AnimationAction.js` | コンストラクタ（9-167行目） |
| 1-2 | constants.js | `src/constants.js` | ループモード定数（LoopRepeat, LoopOnce, LoopPingPong） |

**読解のコツ**: AnimationActionの主要プロパティ
- `_mixer`: 親のAnimationMixer
- `_clip`: 再生するAnimationClip
- `_interpolants`: 各トラックのインターポラント配列
- `_propertyBindings`: PropertyMixer配列
- `loop`, `time`, `timeScale`, `weight`: 再生制御パラメータ

#### Step 2: 再生制御メソッドを理解する

基本的な再生制御フローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AnimationAction.js | `src/animation/AnimationAction.js` | play()（175-181行目） |
| 2-2 | AnimationAction.js | `src/animation/AnimationAction.js` | stop()（188-194行目） |
| 2-3 | AnimationAction.js | `src/animation/AnimationAction.js` | reset()（201-212行目） |

**主要処理フロー**:
1. **175-181行目**: play() - ミキサーにアクションをアクティブ化依頼
2. **188-194行目**: stop() - ミキサーから非アクティブ化後、リセット
3. **201-212行目**: reset() - paused=false, enabled=true, time=0に戻す

#### Step 3: フェード・ワープ機能を理解する

アニメーション遷移機能を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AnimationAction.js | `src/animation/AnimationAction.js` | fadeIn()（305-309行目） |
| 3-2 | AnimationAction.js | `src/animation/AnimationAction.js` | fadeOut()（318-322行目） |
| 3-3 | AnimationAction.js | `src/animation/AnimationAction.js` | crossFadeFrom()（333-352行目） |
| 3-4 | AnimationAction.js | `src/animation/AnimationAction.js` | warp()（470-496行目） |

**主要処理フロー**:
- **305-309行目**: fadeIn - _scheduleFading(duration, 0, 1)で重みを0から1へ
- **318-322行目**: fadeOut - _scheduleFading(duration, 1, 0)で重みを1から0へ
- **333-352行目**: crossFadeFrom - 他のアクションをフェードアウト、自身をフェードイン
- **470-496行目**: warp - タイムスケールを動的に変更

#### Step 4: 内部更新処理を理解する

毎フレーム呼び出される更新処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AnimationAction.js | `src/animation/AnimationAction.js` | _update()（553-629行目） |
| 4-2 | AnimationAction.js | `src/animation/AnimationAction.js` | _updateWeight()（631-666行目） |
| 4-3 | AnimationAction.js | `src/animation/AnimationAction.js` | _updateTimeScale()（668-709行目） |
| 4-4 | AnimationAction.js | `src/animation/AnimationAction.js` | _updateTime()（711-866行目） |

**主要処理フロー**:
- **553-629行目**: _update - メイン更新ループ、インターポラント評価とPropertyMixer蓄積
- **631-666行目**: _updateWeight - フェード処理を含む重み計算
- **668-709行目**: _updateTimeScale - ワープ処理を含むタイムスケール計算
- **711-866行目**: _updateTime - ループ処理を含む時間更新、イベント発火

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

```
AnimationAction
    │
    ├─ play()
    │      └─ _mixer._activateAction(this)
    │
    ├─ stop()
    │      ├─ _mixer._deactivateAction(this)
    │      └─ reset()
    │             ├─ stopFading()
    │             └─ stopWarping()
    │
    ├─ fadeIn() / fadeOut()
    │      └─ _scheduleFading()
    │             └─ _mixer._lendControlInterpolant()
    │
    ├─ crossFadeFrom()
    │      ├─ fadeOutAction.fadeOut()
    │      ├─ this.fadeIn()
    │      └─ warp() [warp=true時]
    │
    ├─ warp()
    │      └─ _mixer._lendControlInterpolant()
    │
    └─ _update() [ミキサーから呼び出し]
           ├─ _updateTimeScale()
           ├─ _updateTime()
           │      ├─ _setEndings()
           │      └─ _mixer.dispatchEvent() [finished/loop]
           ├─ _updateWeight()
           └─ interpolants[].evaluate()
                  └─ propertyMixers[].accumulate()
```

### データフロー図

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

user.play() ───────────▶ AnimationAction.play() ──────▶ ミキサーにアクティブ化

毎フレーム
mixer.update(dt) ──────▶ action._update() ────────────▶ 補間値計算
                                │
                                ▼
                        interpolant.evaluate()
                                │
                                ▼
                        PropertyMixer.accumulate()
                                │
                                ▼
                        Object3Dプロパティ更新

終了時
_updateTime() ─────────▶ mixer.dispatchEvent() ───────▶ 'finished' イベント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AnimationAction.js | `src/animation/AnimationAction.js` | ソース | メインクラス定義 |
| AnimationMixer.js | `src/animation/AnimationMixer.js` | ソース | アクションの管理・更新 |
| AnimationClip.js | `src/animation/AnimationClip.js` | ソース | アニメーションデータ |
| PropertyMixer.js | `src/animation/PropertyMixer.js` | ソース | プロパティ値の蓄積・適用 |
| PropertyBinding.js | `src/animation/PropertyBinding.js` | ソース | プロパティへのバインディング |
| constants.js | `src/constants.js` | ソース | ループモード・ブレンドモード定数 |
