# 通知設計書 20-NOTIFICATION_PHYSICS_PROCESS

## 概要

本ドキュメントは、Godotエンジンにおける `NOTIFICATION_PHYSICS_PROCESS` 通知の設計と実装について記述する。この通知はNodeクラスの基本的な処理ループ通知の一つで、物理処理フェーズで固定時間間隔で発火する、物理シミュレーションの中心となる通知機構である。

### 本通知の処理概要

`NOTIFICATION_PHYSICS_PROCESS` は、Godotのメインループにおいて、物理処理フェーズで送信される通知である。この通知は、set_physics_process(true)が呼ばれたノードに対して、固定時間間隔（デフォルト60Hz）で送信される。物理シミュレーション、移動処理、衝突判定など、一定の時間間隔で実行する必要がある処理を実装するために使用される。

**業務上の目的・背景**：ゲーム開発において、物理シミュレーションや移動処理は、フレームレートに依存しない一定の時間間隔で実行する必要がある。NOTIFICATION_PHYSICS_PROCESSは、固定のデルタタイム（physics_ticks_per_second）で呼び出されるため、物理的に一貫した動作を実現できる。

**通知の送信タイミング**：NOTIFICATION_PHYSICS_PROCESSは、物理ティックごとに送信される。デフォルトでは60Hz（1秒間に60回）で、プロジェクト設定のphysics/common/physics_ticks_per_secondで変更可能。

**通知の受信者**：set_physics_process(true)が呼ばれ、かつシーンツリー内にあるすべてのNodeおよびその派生クラスが受信者となる。処理優先度（physics_process_priority）に基づいてソートされた順序で通知が送信される。

**通知内容の概要**：通知値は整数定数16（Node::NOTIFICATION_PHYSICS_PROCESS）として定義されている。追加のパラメータは含まれないが、get_physics_process_delta_time()でデルタタイムを取得可能。

**期待されるアクション**：受信者は、物理ベースの更新処理を実行する。移動計算、衝突判定、物理シミュレーション、ネットワーク同期などが典型的な処理である。

## 通知種別

Nodeフレーム更新通知（物理処理）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | physics_process_priorityに依存 |
| リトライ | 無 |
| 通知値 | 16 |

### 送信先決定ロジック

set_physics_process(true)が呼ばれたノードに対して、physics_process_priorityの昇順（小さい値が先）で送信される。同じ優先度のノードはツリー順で処理される。

## 通知テンプレート

### 定数定義

```cpp
enum {
    // ...
    NOTIFICATION_PHYSICS_PROCESS = 16,
    NOTIFICATION_PROCESS = 17,
    // ...
};
```

### GDScript使用例

```gdscript
extends CharacterBody2D

@export var speed := 200.0
@export var jump_velocity := -400.0

var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")

func _ready():
    # 物理処理を有効化（_physics_process()をオーバーライドすると自動的に有効化される）
    set_physics_process(true)

func _physics_process(delta: float):
    # 重力の適用
    if not is_on_floor():
        velocity.y += gravity * delta

    # ジャンプ
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = jump_velocity

    # 左右移動
    var direction := Input.get_axis("ui_left", "ui_right")
    if direction:
        velocity.x = direction * speed
    else:
        velocity.x = move_toward(velocity.x, 0, speed)

    # 移動と衝突判定
    move_and_slide()

# または_notification()を使用
func _notification(what):
    if what == NOTIFICATION_PHYSICS_PROCESS:
        var delta = get_physics_process_delta_time()
        _custom_physics_process(delta)

func _custom_physics_process(delta: float):
    # カスタム物理処理
    pass
```

### 典型的な使用パターン

```gdscript
extends RigidBody2D

func _physics_process(delta: float):
    # 物理ボディへの力の適用
    var input_force := Vector2.ZERO

    if Input.is_action_pressed("ui_left"):
        input_force.x -= 1
    if Input.is_action_pressed("ui_right"):
        input_force.x += 1

    apply_central_force(input_force * 500)

    # 速度制限
    if linear_velocity.length() > 300:
        linear_velocity = linear_velocity.normalized() * 300
```

### _process()との使い分け

```gdscript
extends Node2D

func _ready():
    set_process(true)
    set_physics_process(true)

func _process(delta: float):
    # フレームレート依存の処理
    # - UIの更新
    # - アニメーション
    # - 視覚効果
    # - 入力のポーリング（応答性重視）
    $DebugLabel.text = "FPS: %d" % Engine.get_frames_per_second()

func _physics_process(delta: float):
    # 固定時間間隔の処理
    # - 移動計算
    # - 衝突判定
    # - 物理シミュレーション
    # - ネットワーク同期（予測と補間）
    velocity += gravity * delta
    position += velocity * delta
```

## テンプレート変数

NOTIFICATION_PHYSICS_PROCESSはパラメータを持たないため、テンプレート変数は存在しない。ただし、以下のメソッドで関連情報を取得可能。

| メソッド | 戻り値 | 説明 |
|---------|-------|------|
| get_physics_process_delta_time() | double | 物理ティック間隔（秒） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 物理ティック | メインループの物理フェーズ | set_physics_process(true) かつ can_process() | 固定時間間隔で実行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| set_physics_process(false) | 物理処理が無効化されている |
| can_process() == false | ProcessModeによりポーズ/無効状態 |
| シーンツリー外 | ノードがシーンツリーに入っていない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[メインループ] --> B[物理ティック判定]
    B --> C{物理処理時刻到達?}
    C -->|Yes| D[SceneTree::_physics_process]
    C -->|No| E[スキップ]
    D --> F[physics_process_groups処理]
    F --> G{各ノードをイテレート}
    G --> H{can_process?}
    H -->|Yes| I[notification PHYSICS_PROCESS]
    I --> J[GDVIRTUAL_CALL _physics_process]
    J --> K[次のノード]
    H -->|No| K
    K --> G
    G -->|完了| L[物理シミュレーション実行]
    L --> M[アイドル処理フェーズへ]
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（NOTIFICATION_PHYSICS_PROCESSはデータベースを使用しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 特になし | - | - |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 物理ティックレート | physics/common/physics_ticks_per_second（デフォルト60） |
| 最大物理ステップ | physics/common/max_physics_steps_per_frame（デフォルト8） |

### 配信時間帯

制限なし（メインループ実行中、物理ティックごとに送信）

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

- _physics_process()内で長時間のブロッキング処理を行うと物理シミュレーションの精度が低下する
- 重い処理はワーカースレッドやcall_deferred()で非同期化することを推奨
- 無限ループや再帰に注意

## 備考

- _physics_process()をオーバーライドすると、NOTIFICATION_READYで自動的にset_physics_process(true)が呼ばれる
- set_physics_process_priority()で処理順序を制御可能（小さい値が先に実行）
- NOTIFICATION_INTERNAL_PHYSICS_PROCESS（値26）はエンジン内部用の別の処理通知
- 物理補間（Physics Interpolation）を使用すると、物理処理とレンダリングの間のスムーズな補間が可能
- CharacterBody2D/3Dではmove_and_slide()を_physics_process()内で呼び出すことが推奨される

---

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

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

### 推奨読解順序

#### Step 1: 定数定義を理解する

通知値の定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | node.h | `scene/main/node.h` | NOTIFICATION_PHYSICS_PROCESS定数の定義 |

**主要処理フロー**:
- **457行目**: `NOTIFICATION_PHYSICS_PROCESS = 16`

#### Step 2: 通知ハンドラを理解する

_notification()でのPHYSICS_PROCESS処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | node.cpp | `scene/main/node.cpp` | _notification()内のNOTIFICATION_PHYSICS_PROCESS処理 |

**主要処理フロー**:
- **95-97行目**: case NOTIFICATION_PHYSICS_PROCESS
- **96行目**: `GDVIRTUAL_CALL(_physics_process, get_physics_process_delta_time())`

#### Step 3: 処理グループを理解する

SceneTreeでの物理処理グループ管理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | scene_tree.cpp | `scene/main/scene_tree.cpp` | _physics_process関数での処理グループ呼び出し |

#### Step 4: set_physics_process()を理解する

物理処理の有効化/無効化の仕組みを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | node.cpp | `scene/main/node.cpp` | set_physics_process()関数の実装 |

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

```
メインループ
    │
    └─ 物理処理フェーズ
           │
           └─ SceneTree::_physics_process()
                  │
                  └─ PhysicsProcessGroup処理
                         │
                         └─ 各ノード（priority順）
                                │
                                ├─ can_process()チェック
                                │
                                ├─ notification(NOTIFICATION_PHYSICS_PROCESS)
                                │      └─ Node::_notification()
                                │             └─ GDVIRTUAL_CALL(_physics_process, delta)
                                │                    └─ GDScript: _physics_process(delta)
                                │
                                └─ 次のノードへ
           │
           └─ 物理シミュレーション（PhysicsServer2D/3D）
```

### データフロー図

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

物理ティック            ───▶ メインループ           ───▶ 物理フェーズ開始
                                    │
固定デルタタイム        ───▶ physics_delta算出     ───▶ delta値
(1/physics_ticks_per_second)        │
                                    └─▶ SceneTree::_physics_process()
                                            │
                                            ├─▶ priority順ソート
                                            │
                                            └─▶ 各ノードに通知
                                                    │
                                                    └─▶ _physics_process(delta)
                                                            │
                                                            └─▶ 物理計算実行
                                                                    │
                                                                    └─▶ 移動・衝突判定
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| node.h | `scene/main/node.h` | ヘッダ | Node クラス定義、NOTIFICATION_PHYSICS_PROCESS定数 |
| node.cpp | `scene/main/node.cpp` | ソース | _notification()、set_physics_process()実装 |
| scene_tree.h | `scene/main/scene_tree.h` | ヘッダ | SceneTreeクラス定義 |
| scene_tree.cpp | `scene/main/scene_tree.cpp` | ソース | _physics_process()、処理グループ管理 |
| main_loop.h | `core/main/main_loop.h` | ヘッダ | MainLoopクラス定義 |
| physics_server_2d.h | `servers/physics_server_2d.h` | ヘッダ | 2D物理サーバー |
| physics_server_3d.h | `servers/physics_server_3d.h` | ヘッダ | 3D物理サーバー |
| Node.xml | `doc/classes/Node.xml` | ドキュメント | APIリファレンス |
