【Godot】Editable ChildrenとScene継承によるNPCの量産

作成: 2025-06-20最終更新: 2025-12-16

Godotで大量のNPCや敵キャラクターのバリエーションを効率的に作成・管理する方法。シーン継承とEditable Childrenの使い分けを、具体的なコード例とパフォーマンスの観点から解説します。

概要

RPGやアクションゲームを開発していると、必ず直面するのが「キャラクターの量産」という課題です。「スライム」という基本形はできたものの、レベルデザインを進めるうちに「HPが少し多いスライム」「攻撃力が2倍のレッドスライム」「特定のアイテムをドロップするメタルスライム」といった、無数のバリエーションが必要になります。

多くの開発者が最初に試みるのは、ベースとなるシーン(例: Slime.tscn)を単純にコピー&ペーストし、一つひとつ手作業でパラメータを調整する方法でしょう。しかし、このアプローチはすぐに破綻します。もし後から「すべてのスライムの移動速度を少し上げたい」と思ったら、コピーしたすべてのシーンを開いて修正しなければなりません。

この記事では、このような問題をエレガントに解決するGodotの2つの機能、シーン継承(Scene Inheritance)編集可能な子(Editable Children) を解説します。

Editable Childrenの設定方法

方法1: シーン継承 (Scene Inheritance) - 推奨される王道

シーン継承は、オブジェクト指向プログラミングにおける「クラスの継承」の考え方をGodotのシーンシステムに持ち込んだものです。既存のシーンを「親」として、その構造と機能をすべて受け継いだ新しい「子」シーンを作成できます。再利用性と保守性を重視する場合、これが第一の選択肢となります。

シーン継承の仕組みとメリット

  1. 差分のみを管理: 子シーンでは、親シーンから継承したプロパティ(ノードの位置、スクリプトの@export変数など)を自由に変更できます。変更したプロパティは、インスペクタ上で黄色い「元に戻す」アイコンが表示され、親との差分であることが一目でわかります。

  2. 親の変更が子に自動反映: これが最も強力なメリットです。親シーン(BaseEnemy.tscn)の構造を変更したり、スクリプトに新しい機能を追加したりすると、その変更はすべての子シーンに自動的に伝播します。これにより、「全キャラクター共通のバグ修正」と「特定キャラクターの個別調整」を両立できます。

実践的コード例:継承によるバリエーション作成

具体的なコードで見ていきましょう。

1. ベースとなる敵シーンの作成 (BaseEnemy.tscn)

まず、すべての敵の基本となるBaseEnemy.tscnを作成し、以下のようなスクリプトをアタッチします。

# BaseEnemy.gd
extends CharacterBody2D

@export var health: int = 100
@export var attack_power: int = 10
@export var speed: float = 50.0

func _physics_process(delta: float) -> void:
    # シンプルな移動ロジック(ここでは省略)
    pass

func take_damage(amount: int) -> void:
    health -= amount
    print("%s took %d damage, %d HP left." % [self.name, amount, health])
    if health <= 0:
        die()

func die() -> void:
    print("%s has been defeated!" % self.name)
    queue_free()

2. 継承シーンの作成 (StrongEnemy.tscn)

ファイルシステムドックでBaseEnemy.tscnを右クリックし、「新しい継承シーン」を選択してStrongEnemy.tscnとして保存します。この時点では、見た目も機能もBaseEnemyと全く同じです。

3. 継承シーンでのプロパティ上書きと機能拡張

StrongEnemy.tscnを開き、インスペクタを見てみましょう。BaseEnemy.gd@exportしたhealthattack_powerが表示されています。これらの値を直接変更します。

  • Health: 250
  • Attack Power: 25

さらに、StrongEnemyに独自のスクリプトをアタッチして、親の機能を拡張することも可能です。

# StrongEnemy.gd
extends "res://BaseEnemy.gd"

# 親のdie()関数をオーバーライドして、特別な処理を追加
func die() -> void:
    print("A strong enemy lets out a final roar!")
    # super.die() を呼び出すことで、親のdie()関数(queue_free()など)を実行
    super.die()

注意: queue_free()を呼び出すと、ノードは次のフレームでツリーから削除されます。削除されたノードへの参照を保持しているとnull参照エラーが発生する可能性があります。シグナルを接続している場合や、他のノードからの参照がある場合は、削除前にNOTIFICATION_PREDELETEをハンドリングするか、is_instance_valid()で有効性をチェックする習慣をつけましょう。


方法2: 編集可能な子 (Editable Children) - 限定的なケースでの即席カスタマイズ

Editable Childrenは、シーンにインスタンス化した別のシーン(子シーン)の内部ノードを、その場で直接編集可能にする機能です。新しいシーンファイルを作る必要がないため手軽ですが、その変更はその場限りであり、再利用性はありません。

ユースケース

この機能が輝くのは、「このシーンの、この場所に配置した、この一体だけ」を特別にしたい場合です。

  • 特定のボス部屋にいるゴーレム一体だけ、腕のCollisionShape2Dを大きくして攻撃範囲を広げる。
  • チュートリアルで登場する最初の敵一体だけ、スクリプトのspeedを0にして動かなくする。

このような「一度きり」の調整には非常に便利です。

手順

  1. メインシーンにBaseEnemy.tscnをインスタンスとして配置します。
  2. シーンツリーでインスタンス化したBaseEnemyノードを右クリックし、「編集可能な子(Editable Children)」にチェックを入れます。
  3. シーンツリーでBaseEnemyの階層が展開され、内部のSprite2DCollisionShape2Dを直接選択し、インスペクタでプロパティを変更できるようになります。

よくある間違いとベストプラクティス

2つの機能を効果的に使うために、以下の指針を参考にしてください。

よくある間違いベストプラクティス
何でもEditable Childrenで済ませる再利用の可能性があるなら、常にシーン継承を検討する。 「後でまた使うかも」と思ったら、迷わず継承シーンを作成しましょう。
継承階層を深くしすぎる継承は2〜3階層までを基本とする。 それ以上複雑になる場合は、継承よりもコンポーネント(子ノードとして機能を追加)やResourceによるデータ駆動設計を検討すべきです。「親を変更するたびに子の挙動が予測しづらくなる」「どの階層で何が定義されているか追えなくなる」といった兆候が出たら、設計を見直すサインです。
親シーンで子の状態を仮定する親シーンは自己完結させ、子に依存しないように設計する。 親は汎用的な機能を提供し、具体的な振る舞いは子が決定するようにします。
プロパティ変更のためにスクリプトを多用するHP、攻撃力、速度などのパラメータは@export変数を積極的に利用する。 これにより、プログラマでなくてもデザイナーがインスペクタから安全に調整できます。

高度なトピック:パフォーマンスと代替パターン

パフォーマンスへの影響

基本的に、シーン継承もEditable Childrenも、実行時のパフォーマンスに直接的な大きな影響はありません。どちらの方法も、最終的には単一のシーンツリーとしてエンジンに解釈されます。

代替パターン: Resourceによるデータ駆動設計

さらに大規模で複雑なゲーム(例:数百種類のモンスターが登場するRPG)では、シーン継承だけでは管理が煩雑になることがあります。その場合、**Resource**オブジェクトを使ってキャラクターのパラメータをデータとして分離する「データ駆動設計」が有効です。

  1. CharacterData.gdというResourceを継承したスクリプトを作成し、HPや攻撃力などの変数を@exportで定義します。
  2. インスペクタで右クリックし、「新規リソース」からCharacterDataリソースを複数作成します(例: slime_data.tres, goblin_data.tres)。
  3. BaseEnemyシーンのスクリプトに@export var character_data: CharacterDataという変数を追加します。
  4. _ready()関数内で、character_dataリソースからHPや攻撃力の値を読み込み、自身のプロパティに設定します。

この方法なら、敵の種類が増えてもシーンファイルはBaseEnemy.tscnの一つだけで済み、パラメータの調整はリソースファイル(.tres)で行えます。


まとめと次のステップ

キャラクターのバリエーションを作成するための2つの主要なアプローチを解説しました。

  • シーン継承: 再利用可能で保守性の高いテンプレートを作成するための王道。プロジェクトの基本戦略として採用すべきです。
  • Editable Children: 特定のシーンに配置したインスタンス一体だけを微調整するための便利な例外処理

これらの概念をマスターすることで、Godotプロジェクトはより整理され、スケーラブルなものになるでしょう。

次に学ぶべきこと

  • Resourceを使ったデータ駆動設計: 今回紹介した代替パターンをさらに深く学び、より柔軟な設計を目指しましょう。
  • 継承よりコンポジション: オブジェクト指向の原則の一つ。Godotでは、ノードを子として追加していくことで、機能を「合成(Composition)」していく設計が非常に強力です。