はじめに
本記事はGodot Engine初心者の筆者が早めに知っておきたいと感じたGDScriptのノード参照方法に関する記事です。
Godotのバージョンは4.5です。
基本的なノード参照
画像のようにMainシーンの子ノードとしてChildがあり、Childの子ノードとしてGrandchildがあるとします。

この時Godot公式のチュートリアルではMainのスクリプトからChildやGrandchildを参照するには$<ノード名>またはget_node("ノード名")と書くよう教えられます。
extends Node3D
func _ready() -> void:
# $による参照
$Child.do_something()
$Child/Grandchild.do_something()
# get_node()による参照
var child = get_node("Child")
child.do_something()
var grandchild = get_node("Child/Grandchild")
grandchild.do_something()
問題点
しかし、この書き方だとノード名やノードの親子構造に依存してしまうので、シーンの修正に弱いという問題があります。
問題点1. ノードの親子構造の修正
例えばGrandchildをChildからMainの子に移動すると$Child/Grandchildでnullを踏んでしまいます。

extends Node3D
func _ready() -> void:
# $による参照
$Child.do_something()
# ノード構造がChild/Grandchildじゃないので↓でnullを踏む
$Child/Grandchild.do_something()
問題点2. ノード名の修正
また、シーンのノード名をリネームした場合もスクリプト内のノード名ごと修正しないと$Childでnullを踏んでしまいます。

extends Node3D
func _ready() -> void:
# ノード名が異なるので↓でnullを踏む
$Child.do_something()
$Child/Grandchild.do_something()
対処法1. @exportによるノードの割り当て
@exportでインスペクターに公開するとノードの割り当てが可能になります。
Unityでいう[SerializeField]と似た方法です。
この方法であればノード名の変更や親子構造を修正しても参照を保ったままシーン修正が可能です。
extends Node3D
@export var child: Node
@export var grandchild: Node
func _ready() -> void:
# ノードを割り当てればノード名やノードの親子構造の変更に影響されない
child.do_something()
grandchild.do_something()
対処法2. シーン固有ノード(% 記法)
https://docs.godotengine.org/ja/4.x/tutorials/scripting/scene_unique_nodes.html
もう1つがシーン固有ノードに設定する方法です。
(Godot 3.5から追加されたそうです。)
こちらは@exportとは異なり、問題点1.ノードの親子構造の修正のみに対応可能な方法です。
extends Node3D
func _ready() -> void:
# ノード名がChildでシーン固有ノードに設定されている場合
%Child.do_something()
# ノード名がGrandchildでシーン固有ノードに設定されている場合
%Grandchild.do_something()
