Godot 4におけるシーン構成
シーン、スクリプト、リソース
- シーン (
.tscn): ノードとそのリソースからなる再利用可能なツリー。プレイヤー、敵、アイテム、UIパネル、レベルなど。複数回配置するものや単独で実行したいもの (F6) はシーンにします。 - スクリプト (
.gd): 1つのノードにアタッチする動作。制御するシーンのルートノードにアタッチし、親ノードから内部にアクセスしようとするものにはアタッチしません。 - リソーススクリプト (
extends Resourceとclass_nameを使う): ノードを持たないデータ。アイテムのステータス、ウェーブ定義、セーブデータなど。インスペクタで編集し、.tresとして保存します。
シーンは単独で実行しても動作するようにします。外部から何か (ターゲット、スポーン親など) が必要な場合は、get_parent()チェーンを使う代わりに@exportまたはシグナルで公開します。
シーンのインスタンス化
extends Node2D
@export var enemy_scene: PackedScene
@export var spawn_parent: Node2D
func spawn_enemy(at: Vector2) -> void:
var enemy := enemy_scene.instantiate() as Node2D
enemy.position = at
spawn_parent.add_child(enemy)
func load_level(path: String) -> void:
var packed := load(path) as PackedScene
if packed == null:
push_error("Level not found: " + path)
return
get_tree().change_scene_to_packed(packed)
@export var x: PackedSceneはインスペクタで設定され、シーンが参照を保存するためファイル移動後も有効です。const BULLET := preload("res://bullet.tscn")はパース時に解決されます。ファイルがないとパースエラーになります (Preload file "res://bullet.tscn" does not exist.)。固定アセットに適しています。load()は実行時に解決され、パスが間違っているとnullとエラーを返します。実行時にパスを構築する場合に使います。instantiate()(Godot 3のinstance()ではない) はNodeを返すので、必要な型にキャストします。インスタンスはqueue_free()で解放します。
ノードの検索
extends Control
@onready var title: Label = %Title
@onready var start_button: Button = %StartButton
@onready var music: AudioStreamPlayer = $Music
@onready var optional_hint: Label = get_node_or_null("Hint")
func _ready() -> void:
title.text = "Main menu"
start_button.grab_focus()
if optional_hint:
optional_hint.visible = false
music.play()
$Pathはget_node("Path")と同義で、このノードからの相対パスです。ノードがないとエラーになります。省略可能なノードにはget_node_or_null()を使います。%Nameは、同じシーン内で「ユニーク名としてアクセス」とマークされたノード (Sceneドックで右クリック) にどこからでもアクセスでき、シーン内でのリペアレント後も有効です。インスタンス化された子シーンには到達しません。その場合は$Child/%Nameまたは、インスタンス自体がユニークなら%Child/%Nameを使います。@onready変数は_ready()の直前に割り当てられます。_init()や_enter_tree()ではnullです。_ready()は子ノードが親ノードより先に実行されるため、親の_ready()は子ノードを使用できますが、子ノードの_ready()は親の準備ができていることに依存してはいけません。@onready変数には型 (: Label) を指定して、パーサーがメンバー名をチェックできるようにします。型指定なしのパスで$Label.txtと書くと実行時までエラーは検出されません。
グループ
グループはシーンをまたいでノードにタグを付けます。Nodeドック (Groupsタブ) またはコードで追加し、ツリーをクエリします:
extends Node
func _ready() -> void:
add_to_group("enemies")
func alert_all_enemies() -> void:
get_tree().call_group("enemies", "set_alerted", true)
for enemy in get_tree().get_nodes_in_group("enemies"):
print(enemy.name)
var player := get_tree().get_first_node_in_group("player") as Node2D
if player:
print("Player at ", player.global_position)
call_group() はGodot 4では即座に呼び出されます。遅延させるには get_tree().call_group_flags(SceneTree.GROUP_CALL_DEFERRED, ...) を使います。
オートロード
オートロードはメインシーンの前に作成され、シーン変更後も保持されるノードです。1つのシーンを超えて存続する状態 (設定、セーブデータ、シーンスイッチャー) や、シグナルのみのイベントバスに使います。プロジェクト設定 > Globals > Autoload で登録します。project.godot には次のように記述されます:
[autoload]
Events="*res://autoload/events.gd"
* は「有効」がチェックされていることを意味し、スクリプトから名前を直接使用できます (Events.player_died.emit())。オートロードを free() や queue_free() してはいけません。オートロードは少数に留めてください。1つのレベルに属するものは、そのレベルのシーンに配置すべきです。
ツールに関する注意: godot --check-only --script はオートロードをロードしないため、オートロード名を使用するスクリプトは、実際には動作しても Identifier not found: Events と報告します。そのようなスクリプトはシーンを実行して検証してください (godot-verifyスキルを参照)。
class_name
class_name Player はグローバル型を登録するため、他のスクリプトで var player: Player と書いてメンバーチェックができるようになります。エディタは .godot/ にグローバルクラスキャッシュを保持します。エディタ外で class_name を追加した場合は、それを使用するスクリプトをチェックする前にエディタを開くか、godot --headless --path . --import を実行してください。
UID、.uidファイル、ファイル移動
Godot 4はパスだけでなくUIDでもリソースを参照します。シーンとリソースは独自のUIDをファイルヘッダーに、インポートされたアセットは .import ファイルに保存し、Godot 4.4以降ではスクリプトとシェーダーはコンパニオンファイル (player.gd.uid、water.gdshader.uid) を取得します。エディタで保存された参照は次のようになります:
[ext_resource type="Script" uid="uid://dxxoiuob4eoum" path="res://game_manager.gd" id="1_feb5d"]
ルール:
*.uidファイルはバージョン管理にコミットしてください。.gitignoreに追加しないでください。- 可能な場合はGodotのファイルシステムドックでファイルを移動・リネームしてください。参照が更新されます。
- Godot外部 (シェル、IDE、エージェント) でスクリプトやシェーダーを移動する場合は、その
.uidファイルも一緒に移動し、その後エディタを開くかgodot --headless --path . --importを実行してUIDキャッシュが新しいパスを学習するようにしてください。そのスキャンが行われるまでは古いパスが使用され、ロードが失敗します。 .uidファイルを置き去りにすると、Godotは移動したスクリプトに新しいUIDを生成し、古いUIDを参照していたシーンは古いパスにフォールバックします:WARNING: ... invalid UID: uid://... - using text path instead: res://old/path.gd、その後ERROR: ... referenced non-existent resource。.tscnを手書きする場合は、uid=の値を独自に作成しないでください。属性を省略し、正しいpathを使用してください。エディタが次にシーンを保存するときにUIDを追加します。- 手書きのGodot 4シーンは
[gd_scene format=3]で始まり、リソースを文字列IDで参照します:[ext_resource type="Script" path="res://main.gd" id="1_main"]、そしてノード上でscript = ExtResource("1_main")とします。子ノードにはparent="."またはルートからのパスが必要です。Godot 4.7ではノードにunique_id=も書き込まれますが、手書きの場合は省略します。 load("uid://...")とpreload("uid://...")はUID文字列を受け入れます。ファイルシステムドックのコンテキストメニューからUIDをコピーできます。ResourceUID.id_to_text(ResourceLoader.get_resource_uid(path))でコードから取得することもできます。
よくあるミス
get_parent().get_parent()や絶対パス/root/Main/Playerで上にアクセスする。代わりに参照をエクスポートするか、グループを使うか、シグナルを発行します。_init()やノードがツリーに入る前にノードを使用する。- 多くのスクリプトでシーンへの
res://パスをハードコーディングし、PackedSceneをエクスポートしない。 .uidファイルなしで.gdファイルを移動する、または git で*.uidを無視する。- 便利だからといって何でもオートロードにする。