# 通知設計書 29-NOTIFICATION_VISIBILITY_CHANGED

## 概要

本ドキュメントは、Godot EngineのCanvasItemクラスにおける`NOTIFICATION_VISIBILITY_CHANGED`通知の設計仕様を記載する。この通知はノードの可視性が変更された際に発火し、表示状態の変化に応じた処理を実行するための重要な通知である。

### 本通知の処理概要

NOTIFICATION_VISIBILITY_CHANGEDは、CanvasItem派生クラス（Control、Node2Dなど）の可視性（visible/invisible）が変更されたタイミングで発行されるシステム通知である。自身の可視性変更だけでなく、親の可視性変更による間接的な変更でも発火する。

**業務上の目的・背景**：2Dゲーム開発において、ノードの表示/非表示の切り替えは頻繁に行われる。UI要素の表示切り替え、キャラクターの出現/消失、エフェクトの開始/終了など、様々なシチュエーションで可視性変更を検知し、適切な処理（アニメーション開始、サウンド再生、状態更新など）を実行する必要がある。

**通知の送信タイミング**：以下のイベントで送信される：
1. set_visible(true/false)またはshow()/hide()の呼び出し
2. 親ノードの可視性変更による伝播（_propagate_visibility_changed）
3. シーンツリー追加時で可視状態の場合（ENTER_TREE内）

**通知の受信者**：CanvasItemおよびそのすべての派生クラス（Control、Node2D、Polygon2D、Label、Spriteなど）。

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

**期待されるアクション**：
- visibility_changedシグナルの発行
- 表示開始時の初期化処理
- 非表示時のリソース解放
- アニメーションの開始/停止
- オーディオの再生/停止

## 通知種別

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

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高（状態変更通知） |
| リトライ | 無し |

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

1. set_visible()またはshow()/hide()が呼ばれる
2. visible状態が変更される
3. parent_visible_in_treeをチェックし、実際に表示状態が変化する場合のみ処理
4. RenderingServer::canvas_item_set_visible()で描画状態を更新
5. NOTIFICATION_VISIBILITY_CHANGEDを送信
6. 子ノードに対して_propagate_visibility_changed()で伝播

## 通知テンプレート

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

| 項目 | 内容 |
|-----|------|
| 通知ID | 31 |
| 通知名 | NOTIFICATION_VISIBILITY_CHANGED |
| 定義箇所 | scene/main/canvas_item.h |

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

```cpp
void MyCanvasItem::_notification(int p_notification) {
    switch (p_notification) {
        case NOTIFICATION_VISIBILITY_CHANGED: {
            if (is_visible_in_tree()) {
                // 表示された時の処理
                _start_animation();
            } else {
                // 非表示になった時の処理
                _stop_animation();
            }
        } break;
    }
}
```

```gdscript
func _notification(what):
    if what == NOTIFICATION_VISIBILITY_CHANGED:
        if is_visible_in_tree():
            # 表示された時の処理
            _start_animation()
        else:
            # 非表示になった時の処理
            _stop_animation()

# または visibility_changed シグナルを使用
func _ready():
    visibility_changed.connect(_on_visibility_changed)

func _on_visibility_changed():
    pass  # 処理
```

## テンプレート変数

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

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | CanvasItem.set_visible(bool) | 状態が変化した場合 | 可視性設定 |
| API呼び出し | CanvasItem.show() | visible == falseだった場合 | 表示 |
| API呼び出し | CanvasItem.hide() | visible == trueだった場合 | 非表示 |
| 内部伝播 | _propagate_visibility_changed() | parent_visible_in_tree変化時 | 親からの伝播 |
| 自動 | NOTIFICATION_ENTER_TREE | is_visible_in_tree() == true | ツリー追加時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| visible状態が同じ | set_visible()で同じ値を設定した場合 |
| !parent_visible_in_tree && visible変更 | 親が非表示で自身のvisibleを変更した場合（通知は発行されるが表示状態は変化なし） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[set_visible/show/hide呼び出し] --> B{visible変化?}
    B -->|No| C[処理なし]
    B -->|Yes| D[visible更新]
    D --> E{parent_visible_in_tree?}
    E -->|No| F[notification\nVISIBILITY_CHANGED\のみ]
    E -->|Yes| G[_handle_visibility_change]
    G --> H[RenderingServer\ncanvas_item_set_visible]
    H --> I[notification\nVISIBILITY_CHANGED\]
    I --> J{新しい状態?}
    J -->|visible| K[queue_redraw]
    J -->|hidden| L[emit_signal\hidden\]
    K --> M[子ノードへ伝播]
    L --> M
    M --> N[_propagate_visibility_changed\n各子ノード\]
```

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

### 参照データ一覧

| データ | 用途 | 備考 |
|--------|------|------|
| CanvasItem::visible | 自身の可視性設定 | bool |
| CanvasItem::parent_visible_in_tree | 親ツリーの可視性 | bool |

### 更新データ一覧

| データ | 操作 | 概要 |
|--------|------|------|
| CanvasItem::visible | 設定 | 新しい可視性状態 |
| CanvasItem::parent_visible_in_tree | 伝播時に更新 | 親の状態を反映 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| スレッド違反 | メインスレッド以外からの呼び出し | ERR_MAIN_THREAD_GUARDでエラー |

### リトライ仕様

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

## 配信設定

### レート制限

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

### 配信時間帯

制限なし（ゲーム実行中いつでも発生可能）

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

- メインスレッドからのみ可視性変更が許可される
- スクリプトからnotification()を直接呼び出すことは可能だが、実際の可視性状態は変わらない

## 備考

- Node3DにもNOTIFICATION_VISIBILITY_CHANGEDが存在（値は異なる）
- visibility_changedシグナルも同時に発行される
- hiddenシグナルは非表示時のみ発行される
- is_visible_in_tree()で実際の表示状態を確認することが重要

---

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

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

### 推奨読解順序

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

CanvasItemの可視性関連データ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | canvas_item.h | `scene/main/canvas_item.h` | NOTIFICATION_VISIBILITY_CHANGED定数の定義（212行目） |
| 1-2 | canvas_item.h | `scene/main/canvas_item.h` | visible、parent_visible_in_treeフラグ（106-107行目） |

**読解のコツ**: visibleは自身の設定、parent_visible_in_treeは親ツリーの状態を示す。is_visible_in_tree()は両方がtrueの場合のみtrueを返す。

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

可視性変更処理の起点となる関数を特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | canvas_item.cpp | `scene/main/canvas_item.cpp` | set_visible関数（76-90行目） |
| 2-2 | canvas_item.cpp | `scene/main/canvas_item.cpp` | _handle_visibility_change関数（92-111行目） |
| 2-3 | canvas_item.cpp | `scene/main/canvas_item.cpp` | _propagate_visibility_changed関数（67-74行目） |

**主要処理フロー**:
1. **76-90行目**: set_visible()でvisible状態と親状態をチェック
2. **92-111行目**: _handle_visibility_change()でRenderingServer更新と子への伝播
3. **67-74行目**: _propagate_visibility_changed()で子ノードへの再帰的伝播

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

CanvasItem基底クラスでの通知処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | canvas_item.cpp | `scene/main/canvas_item.cpp` | _notification内のVISIBILITY_CHANGEDケース（433-437行目） |

**主要処理フロー**:
- **436行目**: emit_signal(visibility_changed)でシグナル発行

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

```
CanvasItem::set_visible(p_visible) / show() / hide()
    │
    ├─ ERR_MAIN_THREAD_GUARD
    │
    ├─ visible == p_visible チェック
    │
    ├─ visible = p_visible
    │
    └─ _handle_visibility_change(p_visible) [parent_visible_in_tree時]
           │
           ├─ RenderingServer::canvas_item_set_visible()
           │
           ├─ notification(NOTIFICATION_VISIBILITY_CHANGED)
           │      │
           │      └─ CanvasItem::_notification()
           │             └─ emit_signal(visibility_changed)
           │
           ├─ [visible時] queue_redraw()
           │
           ├─ [hidden時] emit_signal(hidden)
           │
           └─ [子ノードへ伝播]
                  └─ CanvasItem::_propagate_visibility_changed(p_visible)
                         │
                         ├─ parent_visible_in_tree = p_visible
                         │
                         └─ [visible && 子がCanvasItem時]
                                └─ 再帰呼び出し
```

### データフロー図

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

set_visible(true/false) ─▶ visible更新 ──────────────────▶ 状態変更
       │                       │
       │                       ▼
       │              parent_visible_in_treeチェック
       │                       │
       │                       ▼
       │              _handle_visibility_change()
       │                       │
       │                       ▼
       │              RenderingServer更新
       │                       │
       └───────────────────────┴───────────────────────▶ NOTIFICATION_VISIBILITY_CHANGED
                                                              │
                                                              ▼
                                                        visibility_changedシグナル
                                                              │
                                                              ▼
                                                        子ノードへ伝播
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| canvas_item.h | `scene/main/canvas_item.h` | ヘッダー | CanvasItem基底クラス定義、通知定数 |
| canvas_item.cpp | `scene/main/canvas_item.cpp` | ソース | set_visible、_handle_visibility_changeの実装 |
| control.cpp | `scene/gui/control.cpp` | ソース | ControlでのVISIBILITY_CHANGED処理 |
| node_2d.cpp | `scene/2d/node_2d.cpp` | ソース | Node2DでのVISIBILITY_CHANGED処理 |
| sprite_2d.cpp | `scene/2d/sprite_2d.cpp` | ソース | Sprite2DでのVISIBILITY_CHANGED処理 |
| animated_sprite_2d.cpp | `scene/2d/animated_sprite_2d.cpp` | ソース | AnimatedSprite2DでのVISIBILITY_CHANGED処理 |
