ノードパスを理解しよう
この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。
課題
「無効なノード参照」、これはGodotヘルプチャンネルで最も頻繁に報告される問題の一つです。ほとんどの場合、以下のようなエラーメッセージとして表示されます。
Invalid get index ‘position’ (on base: ’null instance’).
解決策
問題の核心は「nullインスタンス」部分にあり、これがGodot初心者にとって最も混乱を招く要因となっています。
この問題を回避するには、「ノードパス」の仕組みを理解することが重要です。
ノードパスの理解
シーンツリーはノードで構成されており、これらは親子関係によって接続されています。ノードパスとは、このツリー構造を辿りながらあるノードから別のノードへ移動する際に通る経路のことです。
例として、シンプルな「プレイヤー」シーンを考えてみます。
このシーンのスクリプトは Player ノードに実装されています。もしスクリプトが AnimatedSprite ノードに対して play() メソッドを呼び出す必要がある場合、そのノードへの参照が必要となります。
get_node("AnimatedSprite").play()
get_node() 関数の引数は、対象ノードへのパスを表す文字列です。ここではスクリプト実行中のノードの子要素を指定します。指定したパスが無効な場合、厄介な null instance エラーが発生するほか(さらに「ノードが見つかりませんでした」というメッセージも表示されます)。
ノード参照を get_node() で取得する状況は非常に頻繁にあるため、GDScript にはそのためのショートカットが用意されています。
$AnimatedSprite.play()
get_node() 関数は、対象ノードへの 参照 を返します。
ではここで、より複雑なシーンツリーを見てみてください。
もし Main スクリプトが ScoreLabel にアクセスする必要がある場合、以下のパスを使用してアクセスできます。
get_node("HUD/ScoreLabel").text = "0"
# or using the shortcut:
$HUD/ScoreLabel.text = "0"
ドル記号($)を使用した場合、Godotエディタがパスを自動補完します。またシーンタブでノードを右クリックし、「ノードのパスをコピー」を選択する方法もあります。
ノードへのアクセス先がツリー階層の上位にある場合はどうすればよいでしょうか?その場合は get_parent() 関数を使うか、パス指定に "../" を使用することで親ノードを参照できます。上記の例で、ScoreLabel から Player ノードを取得するには。
get_node("../../Player")
分解して説明します。パス「"../../Player"」は、次のように解釈されます。
- まず一つ上の階層にあるノード(
HUD)を取得します - さらにもう一段階上の階層に移動し(
Main)、その中から子ノードであるPlayerを特定します
見覚えがありますか? ノードパスの仕組みは、オペレーティングシステムのディレクトリパスと完全に同じです。スラッシュ/は 親と子 の関係を示し、..は「1つ上の階層に移動」の意味です。
相対パスと絶対パスの違い
上記の例はすべて相対パスを使用しています。これは現在のノードを起点として、目的地までの経路をたどる形式です。ノードへのパスは絶対パス形式で指定することも可能で、この場合シーンのルートノードを基点とします。
例えば、プレイヤーノードが絶対パスで指定されている場合は。
get_node("/root/Main/Player")
/root は get_tree().root を介してもアクセスできますが、これはシーンのルートノードではありません。これはデフォルトで常に SceneTree に存在するビューポートノードです。
注意事項
上記の例は問題なく動作しますが、後々問題を引き起こす可能性のある注意点があります。以下のような状況を想像してみてください。Playerノードにはhealthプロパティがあり、これをUI内のどこかにあるHealthBarノードに表示したいとします。プレイヤースクリプトに次のように書いたとします。
func take_damage(amount):
health -= amount
get_node("../Main/UI/HealthBar").text = str(health)
初期段階では問題なく機能するかもしれませんが、これは非常に「脆弱」な方式であり、簡単に破綻する可能性があることを意味します。この種の構成には主に2つの重大な問題があります。
- プレイヤーシーン単体でのテストは不可能です。プレイヤーシーンを単独で実行した場合や、UIを持たないテストシーンで使用した場合、
get_node()行が原因でクラッシュが発生します。 - UIの変更はできません。UIのレイアウトを変更するか設計を見直すことにした場合は、パスが無効になるため、必ず修正してください。
この理由から、シーンツリーの 上位 方向へノードパスを指定する操作は避けるべきです。前述の状況では、プレイヤーのHPが変化した時にシグナルを発行するように変更すれば、UIコンポーネントはそのシグナルを受け取って自身の状態を更新できます。この方法なら、ゲームのソースコードに影響を与えることなくノードを自由に再配置・分離することが可能になります。
まとめ
ノードパスの使い方をマスターすれば、必要なノードを簡単に参照できるようになります。慣れればnull instanceエラーメッセージに悩まされることもなくなります。