# 通知設計書 17-NOTIFICATION_EXIT_TREE

## 概要

本ドキュメントは、Godotエンジンにおける `NOTIFICATION_EXIT_TREE` 通知の設計と実装について記述する。この通知はNodeクラスの基本的なライフサイクル通知の一つで、ノードがシーンツリーから削除される時に発火するクリーンアップタイミングを提供する。

### 本通知の処理概要

`NOTIFICATION_EXIT_TREE` は、ノードがシーンツリーから離脱する直前に送信される通知である。この通知を受け取った時点では、まだノードはシーンツリーの一部であり、get_tree()、get_parent()などのメソッドは正常に動作する。通知処理完了後にシーンツリーから切り離される。

**業務上の目的・背景**：ノードがシーンツリーから削除される際に、適切なクリーンアップ処理を実行するタイミングを提供する。NOTIFICATION_EXIT_TREEでは、シグナルの切断、グループからの削除、リソースの解放、状態の保存など、シーンツリー依存のクリーンアップ処理を安全に実行できる。

**通知の送信タイミング**：NOTIFICATION_EXIT_TREEは、以下の状況で送信される。(1) remove_child()によってノードが親から削除される時、(2) 親ノードがシーンツリーから削除される時（子ノードに先に通知）、(3) queue_free()によってノードが削除予定になる時、(4) reparent()によってノードが別の親に移動する時（移動前のEXIT_TREE）。

**通知の受信者**：シーンツリーから削除されるすべてのNodeおよびその派生クラスが受信者となる。子ノードが先に通知を受け取り、その後親ノードが通知を受け取る（深さ優先、逆順）。

**通知内容の概要**：通知値は整数定数11（Node::NOTIFICATION_EXIT_TREE）として定義されている。追加のパラメータは含まれない。

**期待されるアクション**：受信者は、シーンツリー依存のクリーンアップ処理を実行する。具体的には、処理グループからの削除、入力グループからの削除、パスキャッシュのクリア、アクセシビリティ要素の解放などが行われる。

## 通知種別

Nodeライフサイクル通知（内部通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高 |
| リトライ | 無 |
| 通知値 | 11 |

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

シーンツリーから削除されるノードとそのすべての子孫ノードに対して、深さ優先逆順で送信される。子ノードが先に通知を受け取り、その後親ノードが順次通知を受け取る（ENTER_TREEの逆順）。

## 通知テンプレート

### 定数定義

```cpp
enum {
    NOTIFICATION_ENTER_TREE = 10,
    NOTIFICATION_EXIT_TREE = 11,
    // ...
};
```

### GDScript使用例

```gdscript
extends Node

func _exit_tree():
    # シーンツリーから出る時の処理
    print("ノードがシーンツリーから出ます: ", name)
    _cleanup()

func _cleanup():
    # リソースの解放
    # シグナルの切断
    # 状態の保存
    pass

# または_notification()を使用
func _notification(what):
    if what == NOTIFICATION_EXIT_TREE:
        _custom_exit_tree()

func _custom_exit_tree():
    # カスタムクリーンアップ処理
    pass
```

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

```gdscript
extends Node2D

var _connected_player: Node = null

func _enter_tree():
    _connected_player = get_tree().get_first_node_in_group("player")
    if _connected_player:
        _connected_player.health_changed.connect(_on_player_health_changed)

func _exit_tree():
    # シグナルの切断（重要：メモリリーク防止）
    if _connected_player and _connected_player.health_changed.is_connected(_on_player_health_changed):
        _connected_player.health_changed.disconnect(_on_player_health_changed)
    _connected_player = null

func _on_player_health_changed(new_health):
    print("プレイヤーHP: ", new_health)
```

## テンプレート変数

NOTIFICATION_EXIT_TREEはパラメータを持たないため、テンプレート変数は存在しない。

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ノード操作 | remove_child() | ノードがシーンツリー内 | ノードが親から削除される |
| ノード操作 | queue_free() | ノードがシーンツリー内 | ノードが削除予定になる |
| シーン操作 | change_scene_to_*() | 現在シーンがシーンツリー内 | シーンが切り替えられる |
| ノード操作 | reparent() | 現在の親がシーンツリー内 | ノードの親が変更される |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| シーンツリー外 | is_inside_tree()がfalseの場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[remove_child/queue_free呼び出し] --> B{ノードがシーンツリー内?}
    B -->|Yes| C[_propagate_exit_tree開始]
    B -->|No| D[通知なし]
    C --> E[子ノードに再帰（逆順）]
    E --> F[GDVIRTUAL_CALL _exit_tree]
    F --> G[emit_signal tree_exiting]
    G --> H[notification EXIT_TREE true]
    H --> I[tree->node_removed]
    I --> J[親にchild_exiting_tree発火]
    J --> K[グループから削除]
    K --> L[data.viewport = nullptr]
    L --> M[tree->tree_changed]
    M --> N[ready_notified = false]
    N --> O[data.tree = nullptr]
    O --> P[data.depth = -1]
```

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

### 参照テーブル一覧

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

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| get_viewport() null | ビューポートが既にnull | ERR_FAIL_NULLでエラーチェック |
| get_tree() null | シーンツリーが既にnull | ERR_FAIL_NULLでエラーチェック |

### リトライ仕様

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

## 配信設定

### レート制限

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

### 配信時間帯

制限なし

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

- NOTIFICATION_EXIT_TREEはエンジン内部で管理され、ユーザーコードから直接トリガーすることは推奨されない
- _exit_tree()内で長時間の処理を行うとフレームレートに影響するため、重い処理はcall_deferred()で遅延させることを推奨
- ノードの削除はメインスレッドからのみ安全に実行可能

## 備考

- 通知前にtree_exitingシグナルが発火し、親ノードにはchild_exiting_treeシグナルが発火する
- 通知はp_reverse=trueで送信される（逆順通知）
- ノードがシーンツリーから出ると、nodes_in_tree_countがデクリメントされる
- data.path_cacheがクリアされる
- アクセシビリティ要素が解放される
- ready_notifiedがfalseにリセットされる

---

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

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

### 推奨読解順序

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

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

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

**主要処理フロー**:
- **452行目**: `NOTIFICATION_EXIT_TREE = 11`

#### Step 2: 伝播処理を理解する

通知がどのように子ノードに伝播するかを確認する。

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

**主要処理フロー**:
1. **404-451行目**: _propagate_exit_tree関数全体
2. **413-418行目**: 子ノードへの再帰（逆順）
3. **421行目**: `GDVIRTUAL_CALL(_exit_tree)`
4. **423行目**: `emit_signal(SceneStringName(tree_exiting))`
5. **425行目**: `notification(NOTIFICATION_EXIT_TREE, true)` - 逆順通知
6. **426-428行目**: tree->node_removed()
7. **430-434行目**: 親へのchild_exiting_tree発火
8. **436-440行目**: グループからの削除
9. **442行目**: `data.viewport = nullptr`
10. **444-446行目**: tree->tree_changed()
11. **448-450行目**: data.tree, data.depthのクリア

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

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

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

**主要処理フロー**:
1. **186-233行目**: case NOTIFICATION_EXIT_TREE全体
2. **190-201行目**: アクセシビリティ要素の解放
3. **203行目**: nodes_in_tree_countのデクリメント
4. **205-216行目**: 入力グループからの削除
5. **218-225行目**: 処理グループからの削除
6. **229-232行目**: path_cacheの削除

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

```
Node::remove_child() / queue_free()
    │
    └─ Node::_propagate_exit_tree()
           │
           ├─ 子ノードに再帰（逆順）
           │      └─ child._propagate_exit_tree()
           │
           ├─ GDVIRTUAL_CALL(_exit_tree)
           │      └─ GDScript: _exit_tree()
           │
           ├─ emit_signal(tree_exiting)
           │
           ├─ notification(NOTIFICATION_EXIT_TREE, true)
           │      └─ Node::_notification()
           │             ├─ アクセシビリティ解放
           │             ├─ nodes_in_tree_count--
           │             ├─ inputグループ削除
           │             ├─ 処理グループ削除
           │             └─ path_cache削除
           │
           ├─ tree->node_removed()
           │
           ├─ parent.emit_signal(child_exiting_tree)
           │
           ├─ グループから削除
           │
           ├─ data.viewport = nullptr
           │
           ├─ tree->tree_changed()
           │
           ├─ ready_notified = false
           │
           ├─ data.tree = nullptr
           │
           └─ data.depth = -1
```

### データフロー図

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

remove_child(node)      ───▶ _propagate_exit_tree() ───▶ 子ノードに再帰（逆順）
                                    │
                                    ├─▶ _exit_tree()仮想関数
                                    │
                                    ├─▶ tree_exitingシグナル
                                    │
                                    ├─▶ notification(EXIT_TREE, true)
                                    │       │
                                    │       └─▶ 各種クリーンアップ処理
                                    │
                                    ├─▶ node_removed()
                                    │
                                    ├─▶ child_exiting_treeシグナル
                                    │
                                    ├─▶ グループ削除
                                    │
                                    └─▶ 状態クリア
                                            ├─▶ viewport = null
                                            ├─▶ tree = null
                                            └─▶ depth = -1
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| node.h | `scene/main/node.h` | ヘッダ | Node クラス定義、NOTIFICATION_EXIT_TREE定数 |
| node.cpp | `scene/main/node.cpp` | ソース | _propagate_exit_tree()、_notification()実装 |
| scene_tree.h | `scene/main/scene_tree.h` | ヘッダ | SceneTreeクラス定義 |
| scene_tree.cpp | `scene/main/scene_tree.cpp` | ソース | node_removed()等の実装 |
| Node.xml | `doc/classes/Node.xml` | ドキュメント | APIリファレンス |
