Godot Engineに関しての備忘録です。
この記事はGeminiを活用して作成しました。
はじめに
Godot Engine 4でゲームを開発していると、シーン内の特定の「子ノード」や、さらにその奥にある「孫ノード」をスクリプトから操作したい場面がよくあります
「$Node で書くと、階層が変わったときにエラーになる…」
「一番安全に奥のノードにアクセスする方法は?」
今回は、Godot 4(GDScript)において、子・孫ノードをスマートかつ堅牢(エラーが起きにくい)に取得する方法をパターン別に解説します
取得方法まとめ
まずは結論から。目的の階層や用途に合わせて使い分けるのがベストです
| 取得したい対象 | おすすめの方法 | 特徴 |
|---|---|---|
| 同じシーン内の孫・ひ孫 | %UniqueName |
構造変更に一番強い!最もおすすめ |
| 直下の子(名前固定) | $NodeName |
最も手軽。直下用 |
| 階層が固定された孫 | $Child/Grandchild |
シンプルだが階層の変化に弱い |
| 特定の型の孫ノード群 | find_children() |
動的に特定のノードを探す場合に最適 |
ゲームの仕様変更(階層の整理や整理にともなうノード移動)はよくあることなので、同じシーン内であれば**「ユニークノード(%記法)」を使うのが圧倒的に安全**です
1. 基本:特定の子要素(1代下)を取得する
まずは基本となる、直下の子ノードを取得する一般的な方法です
1-1. パス(名前)で指定する:$ 記法 / get_node()
Godotで最もよく使われる、ノード名を使った取得方法です
extends Node2D
func _ready():
# 「Weapon」という名前の直下の子ノードを取得
var weapon = $Weapon
# または以下も同等
var weapon_alt = get_node("Weapon")
- メリット: 直感的でコードが短く済むため、最も手軽
-
注意点: 子ノードの名前を変更したり、階層を移動させると
nullになりエラーになります
1-2. インデックス(順番)で指定する:get_child()
名前ではなく、ヒエラルキー(シーンツリー)の上からの順番で取得します
# 0番目の子(1つ目の子ノード)を取得
var first_child = get_child(0)
- メリット: ノード名が変わっても影響を受けません
- 注意点: インスペクター側でノードの並び順を入れ替えると、取得できるノードが変わってしまいます
2. 応用:孫要素(2代以上下)を取得する
さらに深い階層(孫やひ孫)を取得する方法です。Godot 4ではいくつかの強力なアプローチがあります
2-1. 【推奨】シーン固有の「ユニークノード(%記法)」を使う
Godot 4で孫ノードを取得する際のベストプラクティスがこれです
- エディタのシーンツリーで、目的の孫ノードを右クリック
-
「シーン固有の名前としてアクセス」(Access as Unique Name)にチェックを入れる(ノード名の横に
%マークがつきます)
スクリプトからは、中間の階層をすべて無視して名前だけで一発でアクセスできるようになります
# 「Body」の中の「Hand」の中の「Sword」であっても、ユニーク名なら一発
func _ready():
var sword = %Sword
-
メリット: ノード名が変わっても影響を受けません
途中の階層構造(親や子)をどれだけ変更・移動させても、同じシーン内であれば絶対にコードが壊れません - 注意点: 異なるシーン(別ファイルとして保存された別シーン)をまたいで取得することはできません
2-2. 階層パスをそのまま指定する
ユニークノードを使わない(使えない)場合は、スラッシュ(/)で区切ってパスを指定します
# パスをそのまま繋いで孫ノードを取得
func _ready():
var sword = $Body/Hand/Sword
- メリット: 構造が直感的にわかりやすい
- 注意点: 途中の「Body」や「Hand」の名前が変わったり、階層から外れたりすると即座にエラーになります
2-3. スクリプト(型)で動的に探す:find_children()
「名前や階層は変わるかもしれないけれど、孫ノードの中に潜んでいる**特定の型のノード(例: Sprite2DやAnimationPlayer)**を取得したい」という場合に強力なメソッドです
func _ready():
# 子・孫以下すべての階層から、型が「Sprite2D」のものを検索して配列で取得
# 第1引数は名前(*はワイルドカード)、第2引数は型、第3引数をtrueにすると孫以下も検索
var sprites = find_children("*", "Sprite2D", true)
for sprite in sprites:
print(sprite.name)
- メリット: 階層構造がどれだけ変化しても、目的の型を自動で走査してくれます
3. 【重要】設計思想
Godot 4でノードを取得する際、バグを減らすための設計思想です
-
「上向きのシグナル、下向きの関数呼び出し」を意識する
親から子・孫を操作する場合は、今回紹介した方法で直接ノードを取得して関数を呼び出して問題ありません。しかし、孫から親(あるいはさらに上のルート)を操作したい場合は、ノードを直接取得せず「シグナル(Signal)」を発行して親側に検知させるのがGodotの綺麗な設計(疎結合)です -
@onreadyを活用する
ノードの取得は、ノードツリーが組み立てられた直後(_ready()時)に行う必要があります。変数の宣言時に@onreadyをつけることで、コードをすっきりさせることができます
# メンバ変数として最初に定義しておく
@onready var sword = %Sword
func _process(delta):
# 毎フレーム安全にアクセスできる
sword.rotation += delta