# 通知設計書 24-NOTIFICATION_UNPARENTED

## 概要

本ドキュメントは、Godot EngineのNodeクラスにおける`NOTIFICATION_UNPARENTED`通知の設計仕様を記載する。この通知はノードが親ノードから削除された際に発火し、ノードの階層構造変更を検知するための重要なライフサイクル通知である。

### 本通知の処理概要

NOTIFICATION_UNPARENTEDは、ノードが親ノードから削除される際に発行されるシステム通知である。この通知は親子関係の解除を検知するための通知で、シーンツリーからの離脱（NOTIFICATION_EXIT_TREE）の後に発火する。

**業務上の目的・背景**：ゲーム開発において、ノードが親から切り離される際に、親との関連付けを解除するクリーンアップ処理が必要な場合がある。NOTIFICATION_UNPARENTEDは、ノードが完全に切り離される前の最終的なクリーンアップ機会を提供する。物理コリジョンの解除、ナビゲーションエージェントの無効化など、親に依存する機能の適切な終了処理に使用される。

**通知の送信タイミング**：ノードがremove_child()によって親ノードから削除される際。具体的には_set_tree(nullptr)とremove_child_notify()の後、data.parentがnullptrに設定される前に送信される。この時点ではまだdata.parentは設定されているが、シーンツリーからは既に離脱している。

**通知の受信者**：親ノードから削除される子ノード自身。親ノードには送信されない（親ノードにはremove_child_notify仮想関数が呼ばれる）。

**通知内容の概要**：通知IDは整数値19（NOTIFICATION_UNPARENTED = 19）。追加のパラメータは含まない。

**期待されるアクション**：
- 親ノードへの参照を使用した最終処理
- 親に依存するリソースの解放
- 物理コリジョンシェイプの親からの解除
- ナビゲーションエージェントの無効化
- スケルトンモディファイアの解除

## 通知種別

エンジン内部通知（Engine Internal Notification）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高（ライフサイクル通知） |
| リトライ | 無し |

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

1. remove_child()が呼ばれる
2. data.blocked++ でブロック状態に
3. _set_tree(nullptr)でシーンツリーから離脱
4. remove_child_notify(p_child)が呼ばれる
5. 子ノードにNOTIFICATION_UNPARENTEDを送信
6. data.blocked-- でブロック解除
7. data.parentがnullptrに設定される

## 通知テンプレート

### システム通知の場合

| 項目 | 内容 |
|-----|------|
| 通知ID | 19 |
| 通知名 | NOTIFICATION_UNPARENTED |
| 定義箇所 | scene/main/node.h |

### 通知ハンドリング例

```cpp
void MyNode::_notification(int p_notification) {
    switch (p_notification) {
        case NOTIFICATION_UNPARENTED: {
            // 親から切り離される際の処理
            Node *parent = get_parent(); // まだアクセス可能
            if (parent) {
                // 親への参照を使った最終処理
            }
        } break;
    }
}
```

```gdscript
func _notification(what):
    if what == NOTIFICATION_UNPARENTED:
        var parent = get_parent()  # まだアクセス可能
        if parent:
            # 親への参照を使った最終処理
            pass
```

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| p_notification | 通知ID（19） | エンジン内部 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | Node.remove_child(child) | 常に | 子ノード削除時 |
| 内部処理 | NOTIFICATION_PREDELETEハンドラ | 親がいる場合 | ノード削除時の自動remove_child |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| なし | 親からの削除時は常に送信される |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[remove_child呼び出し] --> B[バリデーションチェック]
    B --> C{data.blocked > 0?}
    C -->|Yes| D[エラー: ブロック中]
    C -->|No| E[data.blocked++]
    E --> F[_set_tree\nullptr\]
    F --> G[NOTIFICATION_EXIT_TREE送信]
    G --> H[remove_child_notify呼び出し]
    H --> I[NOTIFICATION_UNPARENTED送信]
    I --> J[data.blocked--]
    J --> K[children HashMapから削除]
    K --> L[data.parent = nullptr]
    L --> M[data.index = -1]
    M --> N[NOTIFICATION_CHILD_ORDER_CHANGED送信]
    N --> O[_propagate_after_exit_tree]
    O --> P[終了]
```

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

### 参照データ一覧

| データ | 用途 | 備考 |
|--------|------|------|
| Node::data.parent | 親ノードの参照 | この通知時点ではまだ設定されている |
| Node::data.tree | シーンツリーの参照 | この通知時点ではnullptr |
| Node::data.name | ノード名 | まだ設定されている |

### 更新データ一覧

| データ | 操作 | 概要 |
|--------|------|------|
| Node::data.parent | クリア | nullptr（通知後に設定） |
| Node::data.index | リセット | -1（通知後に設定） |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ブロック中 | data.blocked > 0 | エラーログ出力、call_deferred推奨 |
| 親不一致 | p_child->data.parent != this | ERR_FAIL_COND |
| スレッド違反 | ツリー内でメインスレッド以外から | エラーログ出力、call_deferred推奨 |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（エンジン実行中いつでも発生可能）

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

- メインスレッドからのみ子ノード削除が許可される（ツリー内のノードの場合）
- スクリプトからnotification()を直接呼び出すことは可能だが、状態の不整合を招く可能性がある

## 備考

- NOTIFICATION_EXIT_TREEの後に発火する
- NOTIFICATION_PARENTEDとペアで使用される対照的な通知
- この通知時点ではdata.parentはまだ設定されているが、get_tree()はnullを返す
- 物理系コンポーネントでは親コリジョンオブジェクトとの関連解除に使用される

---

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

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

### 推奨読解順序

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

ノードの親子関係に関するデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | node.h | `scene/main/node.h` | NOTIFICATION_UNPARENTED定数の定義（460行目） |
| 1-2 | node.h | `scene/main/node.h` | Data構造体のparent、indexフィールド |

**読解のコツ**: NOTIFICATION_PARENTED（18）の次の値としてUNPARENTED（19）が定義されていることを確認。

#### Step 2: エントリーポイントを理解する

処理の起点となる関数を特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | node.cpp | `scene/main/node.cpp` | remove_child関数（1735-1772行目）の処理フロー |

**主要処理フロー**:
1. **1736行目**: メインスレッドチェック
2. **1738-1739行目**: ブロック状態と親チェック
3. **1751行目**: data.blocked++ でブロック開始
4. **1752行目**: _set_tree(nullptr)でツリーから離脱
5. **1754行目**: remove_child_notify(p_child)
6. **1755行目**: p_child->notification(NOTIFICATION_UNPARENTED)
7. **1757行目**: data.blocked-- でブロック解除
8. **1763-1764行目**: data.parent = nullptr; data.index = -1;

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

派生クラスでの通知処理例を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | collision_shape_2d.cpp | `scene/2d/physics/collision_shape_2d.cpp` | コリジョンシェイプの親解除処理 |
| 3-2 | collision_shape_3d.cpp | `scene/3d/physics/collision_shape_3d.cpp` | 3Dコリジョンシェイプの親解除処理 |
| 3-3 | skeleton_modifier_3d.cpp | `scene/3d/skeleton_modifier_3d.cpp` | スケルトンモディファイアの親解除処理 |

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

```
Node::remove_child(p_child)
    │
    ├─ ERR_FAIL_COND チェック群
    │      ├─ メインスレッドチェック
    │      ├─ ブロック状態チェック
    │      └─ 親一致チェック
    │
    ├─ data.blocked++
    │
    ├─ p_child->_set_tree(nullptr)
    │      └─ NOTIFICATION_EXIT_TREE
    │
    ├─ remove_child_notify(p_child) [仮想関数]
    │
    ├─ p_child->notification(NOTIFICATION_UNPARENTED)
    │      └─ [派生クラスの_notification]
    │
    ├─ data.blocked--
    │
    ├─ data.children.erase(p_child->data.name)
    │
    ├─ p_child->data.parent = nullptr
    │
    ├─ p_child->data.index = -1
    │
    ├─ notification(NOTIFICATION_CHILD_ORDER_CHANGED)
    │
    └─ p_child->_propagate_after_exit_tree()
```

### データフロー図

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

Node* p_child ──────────▶ remove_child ─────────────────▶ NOTIFICATION_EXIT_TREE
       │                       │                                  │
       │                       ▼                                  ▼
       │              _set_tree(nullptr)              NOTIFICATION_UNPARENTED
       │                       │                                  │
       │                       ▼                                  ▼
       │              remove_child_notify              [子ノードで処理]
       │                       │
       └───────────────────────┴───────────────────────▶ data.parent = nullptr
                                                        data.index = -1
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| node.h | `scene/main/node.h` | ヘッダー | Node基底クラス定義、通知定数 |
| node.cpp | `scene/main/node.cpp` | ソース | remove_childの実装 |
| collision_shape_2d.cpp | `scene/2d/physics/collision_shape_2d.cpp` | ソース | 2Dコリジョンシェイプの親解除処理 |
| collision_shape_3d.cpp | `scene/3d/physics/collision_shape_3d.cpp` | ソース | 3Dコリジョンシェイプの親解除処理 |
| collision_polygon_2d.cpp | `scene/2d/physics/collision_polygon_2d.cpp` | ソース | 2Dコリジョンポリゴンの親解除処理 |
| collision_polygon_3d.cpp | `scene/3d/physics/collision_polygon_3d.cpp` | ソース | 3Dコリジョンポリゴンの親解除処理 |
| skeleton_modifier_3d.cpp | `scene/3d/skeleton_modifier_3d.cpp` | ソース | スケルトンモディファイアの親解除処理 |
| navigation_agent_2d.cpp | `scene/2d/navigation/navigation_agent_2d.cpp` | ソース | ナビゲーションエージェントの親解除処理 |
| control.cpp | `scene/gui/control.cpp` | ソース | Controlノードの親解除処理 |
