# 通知設計書 73-script_changed

## 概要

本ドキュメントは、Godot Engineにおける`script_changed`シグナルの設計仕様を記載する。このシグナルはObjectクラスで定義され、オブジェクトにアタッチされたスクリプトが変更された際に発火する。すべてのObjectとその派生クラス（Node、Resource等）がこのシグナルを持つ。

### 本通知の処理概要

このシグナルは、オブジェクトの`set_script()`メソッドが呼び出され、スクリプトが正常に変更された後に発火する。スクリプトの追加、変更、削除のいずれの場合も発火し、エディタやランタイムでのスクリプト動的変更を監視できる。

**業務上の目的・背景**：ゲーム開発やエディタプラグインにおいて、オブジェクトのスクリプトが動的に変更された際に、UIの更新やキャッシュのクリア、依存関係の再構築などのリアクティブな処理が必要となる。このシグナルにより、スクリプト変更を監視し、適切な対応を行うことができる。

**通知の送信タイミング**：以下のイベントでシグナルが発火する：
- `Object.set_script()`メソッドでスクリプトが設定された時
- スクリプトがnullに設定された時（スクリプト削除）
- エディタでスクリプトがアタッチ/デタッチされた時

**通知の受信者**：シグナルに接続したすべてのオブジェクト。主な利用者：
- エディタUI（インスペクター、シーンツリー）
- スクリプトエディタ
- カスタムエディタプラグイン
- ランタイムでのスクリプト監視システム

**通知内容の概要**：シグナル自体にはパラメータがない。新しいスクリプトは`get_script()`で取得可能。

**期待されるアクション**：シグナル受信者は以下のような処理を行う：
1. オブジェクトのプロパティリスト更新
2. UIの再描画
3. キャッシュされたスクリプト情報のクリア
4. 依存関係グラフの更新

## 通知種別

シグナル（Signal）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| シグナル名 | `script_changed` |
| 定義クラス | Object |
| 送信方式 | 同期（emit_signal） |
| 優先度 | 中 |
| リトライ | なし |

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

シグナルは以下の手順で送信される：
1. `Object.set_script()`が呼び出される
2. スクリプトインスタンスが正常に設定/変更/削除される
3. `notify_property_list_changed()`が呼び出される
4. `emit_signal(CoreStringName(script_changed))`でシグナル発火

## 通知テンプレート

### シグナル定義

```cpp
// object.cpp
ADD_SIGNAL(MethodInfo("script_changed"));
```

### 処理テンプレート（GDScript）

```gdscript
func _ready():
    # シグナル接続
    script_changed.connect(_on_script_changed)

func _on_script_changed():
    print("スクリプトが変更されました")
    var new_script = get_script()
    if new_script:
        print("新しいスクリプト: ", new_script.resource_path)
    else:
        print("スクリプトが削除されました")
```

### 処理テンプレート（C++）

```cpp
// シグナル接続
object->connect("script_changed", callable_mp(this, &MyClass::_on_script_changed));

void MyClass::_on_script_changed() {
    Ref<Script> new_script = object->get_script();
    if (new_script.is_valid()) {
        // 新しいスクリプトの処理
    }
}
```

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| なし | シグナルにはパラメータがない | - | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | Object.set_script() | スクリプト設定成功 | 明示的なスクリプト設定 |
| エディタ操作 | スクリプトアタッチ | スクリプト設定成功 | エディタでのD&D等 |
| エディタ操作 | スクリプトデタッチ | スクリプト削除成功 | エディタでの削除操作 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 同一スクリプトの再設定 | スクリプトインスタンスが変更されない場合 |
| is_blocking_signals() | シグナルブロック中 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[set_script呼び出し] --> B{スクリプト検証}
    B -->|無効| C[エラー処理]
    B -->|有効| D{既存スクリプトと同一?}
    D -->|Yes| E[処理終了・シグナルなし]
    D -->|No| F[既存スクリプトインスタンス削除]
    F --> G[新スクリプトインスタンス作成]
    G --> H[notify_property_list_changed]
    H --> I[emit_signal script_changed]
    I --> J[接続先に通知]
    J --> K[終了]
    C --> K
    E --> K
```

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

該当なし（本シグナルはエンジン内部のオブジェクトシステムで処理され、データベースは使用しない）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 無効なスクリプト | 指定されたスクリプトがコンパイルエラー | エラーメッセージ出力、スクリプト未設定 |
| クラス不一致 | スクリプトの継承元とオブジェクト型が不一致 | エラーメッセージ出力 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 制限 | なし |

### 配信時間帯

制限なし

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

- スクリプト変更はオブジェクトの動作を根本的に変えるため、信頼できないソースからのスクリプト設定には注意
- シグナルハンドラ内で新しいスクリプトにアクセスする際、スクリプトはまだ初期化されていない可能性がある（CONNECT_DEFERREDの使用推奨）

## 備考

- 公式ドキュメントにて「新しいスクリプトはまだ初期化されていない可能性があるため、CONNECT_DEFERREDでの接続を推奨」と記載
- このシグナルは`property_list_changed`シグナルと連動して発火する（スクリプト変更によりプロパティリストが変わるため）

---

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

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

### 推奨読解順序

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

Objectクラスのスクリプト関連構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | object.h | `core/object/object.h` | script_instanceメンバと関連API |
| 1-2 | script_language.h | `core/object/script_language.h` | ScriptInstanceクラスの定義 |

**読解のコツ**: `script_instance`はScriptInstanceポインタで、スクリプトの実行環境を保持する。`set_script()`でRefCountedとしてのScriptとScriptInstanceの両方が管理される。

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

シグナルの発火元を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | object.cpp | `core/object/object.cpp` | set_script()メソッド実装（行1090付近） |

**主要処理フロー**:
1. **行1091-1094**: プレースホルダーインスタンス作成処理
2. **行1096**: `notify_property_list_changed()`呼び出し
3. **行1097**: `emit_signal(CoreStringName(script_changed))`

#### Step 3: シグナル定義を理解する

シグナルの登録処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | object.cpp | `core/object/object.cpp` | _bind_methods()内のADD_SIGNAL（行2006） |

**主要処理フロー**:
- **行2006**: `ADD_SIGNAL(MethodInfo("script_changed"));`

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

```
Object::set_script(Variant p_script)
    │
    ├─ スクリプト検証
    │      ├─ RefCounted::cast_to<Script>()
    │      └─ Script::can_instantiate()
    │
    ├─ 既存スクリプトインスタンス削除
    │      └─ memdelete(script_instance)
    │
    ├─ 新スクリプトインスタンス作成
    │      └─ Script::instance_create(this)
    │
    ├─ notify_property_list_changed()
    │      └─ emit_signal("property_list_changed")
    │
    └─ emit_signal("script_changed")
           └─ 接続先Callableの呼び出し
```

### データフロー図

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

Ref<Script>    ───▶ set_script()          ───▶ script_changed
 - GDScript         │                          シグナル発火
 - C#Script         ├─ バリデーション               │
 - null             ├─ インスタンス作成              ▼
                    └─ シグナル発火            property_list
                                              _changed発火
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| object.h | `core/object/object.h` | ヘッダ | Objectクラス定義、スクリプト関連API宣言 |
| object.cpp | `core/object/object.cpp` | ソース | set_script()実装、シグナル定義 |
| script_language.h | `core/object/script_language.h` | ヘッダ | Script、ScriptInstanceクラス定義 |
| Object.xml | `doc/classes/Object.xml` | ドキュメント | 公式ドキュメント定義（行1022-1027） |
| scene_tree_editor.cpp | `editor/scene/scene_tree_editor.cpp` | ソース | エディタでの利用例 |
| script_editor_plugin.cpp | `editor/script/script_editor_plugin.cpp` | ソース | スクリプトエディタでの利用例 |
