【Godot】ボタンでシーンを切り替える方法|タイトル画面とゲーム画面を往復する
この記事について
タイトル画面の「ゲームを始める」を押して、別のシーンへ移動したい。ゲーム画面からタイトルにも戻りたい。そんなときは、SceneTreeのchange_scene_to_file()で実行中のシーンを入れ替えます。
この記事では、タイトル画面とクリック回数を表示するゲーム画面を作り、ボタンで往復します。切り替え先のファイルパス、実行するシーンの選び方、画面を戻したときに変数が初期化される理由も確認します。Godot 4.6.3のGDScriptを使い、項目名は英語表記で説明します。外部画像は不要です。
この記事でわかること
- 2つの.tscnファイルを作り、ボタンから切り替える方法
- res://から始まるファイルパスと、ノードの相対パスの違い
- 切り替えに失敗した場合の戻り値と、変数がリセットされる仕組み
基本手順
手順1:タイトル画面を作り、title.tscnとして保存する
新しいプロジェクトをCompatibilityで作成します。Project → Project Settings → Display → WindowのViewport Widthを640、Viewport Heightを480にします。Scene → New SceneでUser Interfaceを選び、ControlをTitleに名前変更します。Ctrl+Sでプロジェクト直下へtitle.tscnとして保存してください。
Titleの子にVBoxContainerを追加し、Contentと名付けます。その子にLabel、Button、Labelをこの順番で追加し、Heading、StartButton、StatusLabelに名前変更します。
Title (Control) └ Content (VBoxContainer) ├ Heading (Label) ├ StartButton (Button) └ StatusLabel (Label)
ContentのInspectorでLayout → TransformのPositionをX=40、Y=40、SizeをX=400、Y=260にします。Theme Overrides → Constants → Separationを16にします。HeadingのTextは「タイトル画面」、StartButtonのTextは「ゲームを始める」、StatusLabelのTextは空欄にします。
StartButtonのLayout → Custom Minimum SizeのYを56にします。3つの子ノードのTheme Overrides → Font Sizes → Font Sizeを24にすると読みやすくなります。StatusLabelは普段は空欄ですが、切り替えに失敗した場合に理由を表示します。

手順2:ゲーム画面を別のシーンとして保存する
Scene → New Sceneから、もう一度User Interfaceを選びます。今度はControlをGameに変更し、プロジェクト直下へgame.tscnとして保存します。Titleの子としてGameを追加するのではなく、独立したシーンを作ってください。
Game (Control) └ Content (VBoxContainer) ├ Heading (Label) ├ CountLabel (Label) ├ CountButton (Button) ├ BackButton (Button) └ StatusLabel (Label)
Gameの子にContentというVBoxContainerを作り、手順1と同じ位置・幅・Separationを設定します。上の順番で子ノードを追加します。HeadingのTextは「ゲーム画面」、CountLabelは「クリック回数: 0」、CountButtonは「1回増やす」、BackButtonは「タイトルへ戻る」です。StatusLabelは空欄にします。
2つのButtonのCustom Minimum SizeのYを56、各LabelとButtonのFont Sizeを24にします。FileSystemドックでtitle.tscnとgame.tscnが両方ともres://の直下にあることを確認してください。ノード名のTitle・Gameと、ファイル名のtitle.tscn・game.tscnは別のものです。

手順3:タイトルの開始ボタンからgame.tscnを開く
title.tscnを開き、Titleを選んでAttach Scriptからres://title.gdを作ります。Templateのチェックを外して作成し、内容を次のコードに置き換えます。今回のシグナルはコードで接続するため、Signalsタブから追加接続する必要はありません。
extends Control
@onready var status_label: Label = $Content/StatusLabel
func _ready() -> void:
$Content/StartButton.pressed.connect(_on_start_pressed)
func _on_start_pressed() -> void:
var error: Error = get_tree().change_scene_to_file("res://game.tscn")
if error != OK:
status_label.text = "ゲーム画面を開けません: %s" % error_string(error)
_ready()はシーンが準備できたときに呼ばれます。この中でStartButtonのpressedを接続し、実際にボタンを押したときだけ_on_start_pressed()が動くようにします。
change_scene_to_file(“res://game.tscn”)のres://はプロジェクトのフォルダーを表します。一方、$Content/StartButtonはTitleから見たノードの道順です。シーン切り替え先にはノードの名前ではなく、保存した.tscnのファイルパスを書きます。
戻り値のerrorがOKなら切り替えは受け付けられています。OK以外ならStatusLabelへエラー内容を出します。この戻り値は移動先のノードではないので、LabelやButtonとして操作しないでください。

手順4:ゲーム画面に加算処理と戻る処理を書く
game.tscnのGameへres://game.gdを付け、次のコードを入力します。CountButtonとBackButtonは、それぞれ別の関数へ接続します。
extends Control
var count: int = 0
@onready var count_label: Label = $Content/CountLabel
@onready var status_label: Label = $Content/StatusLabel
func _ready() -> void:
$Content/CountButton.pressed.connect(_on_count_pressed)
$Content/BackButton.pressed.connect(_on_back_pressed)
func _on_count_pressed() -> void:
count += 1
count_label.text = "クリック回数: %d" % count
func _on_back_pressed() -> void:
var error: Error = get_tree().change_scene_to_file("res://title.tscn")
if error != OK:
status_label.text = "タイトルを開けません: %s" % error_string(error)
「1回増やす」を押すとcountが増え、「タイトルへ戻る」を押すとtitle.tscnを開きます。保存後にDebuggerへ赤いエラーが出ていないか確認してください。$Content/CountLabelなどのパスが見つからない場合は、手順2のノード名と親子関係を見直します。
手順5:タイトルから実行し、往復と初期化を確認する
title.tscnのタブを開き、Run Current Scene(Windows・Linuxの標準ショートカットはF6)を押します。「ゲームを始める」をクリックし、ゲーム画面が表示されたら「1回増やす」を3回押します。クリック回数が3になったら「タイトルへ戻る」を押してください。

もう一度「ゲームを始める」を押すと、クリック回数は0です。元のGameがそのまま隠れて待っているのではなく、新しいGameのインスタンスが作られ、countが初期値の0になります。

F5のRun Projectでもタイトルから始めたい場合は、FileSystemでtitle.tscnを右クリックし、Set As Main Sceneを選びます。F6は現在編集中のシーン、F5はプロジェクトのメインシーンを実行する点に注意してください。
うまくいかないときの確認箇所
ボタンは押せるのに、画面が切り替わらない
まず画面下部のエラー表示とDebuggerを見ます。title.gdの移動先がres://game.tscnになっているか、FileSystem上の実際の保存先と大文字・小文字まで一致しているか確認してください。例えばscenesフォルダーへ保存したなら、res://scenes/game.tscnへ変更する必要があります。
FileSystemのファイルを右クリックし、Copy Pathからパスをコピーすると入力間違いを減らせます。まだ保存していないシーンは、保存してから指定します。ERR_CANT_OPENは読み込めない場合、ERR_CANT_CREATEはシーンをインスタンス化できない場合の戻り値です。
切り替え処理が勝手に実行される
pressed.connect(_on_start_pressed)の関数名の後ろへ()を付けていないか確認します。_ready()内でchange_scene_to_file()を直接実行すると、ボタンを待たずにシーンが切り替わります。
ゲームへ戻ると点数や状態が消える
今回のcountはGameのスクリプトが持つ変数なので、シーンの作り直しで0に戻ります。画面をまたいで維持したい情報はAutoloadなどの共有場所へ置く設計が必要です。また、アプリ終了後も残すにはファイル保存が必要で、シーン切り替えだけでは保存されません。
切り替えた直後のコードでget_tree()がnullになる
シーン切り替えでは元のシーンがツリーから外されます。元のシーンのスクリプトで、切り替え後もget_tree()や子ノードを使い続ける構造は避けましょう。移動先の初期化は、そのシーンの_ready()へ書くと整理しやすくなります。
まとめ
ボタンによるシーン切り替えは、pressedを接続し、change_scene_to_file()へ保存済みの.tscnのパスを渡す流れです。まずタイトルとゲームを往復できるようにし、次に変数が初期化される場所を確認してください。動かない場合は、ボタンの接続・ファイルパス・戻り値の順に調べると原因を絞り込めます。
参考:Godot 4.6公式ドキュメント:SceneTree.change_scene_to_file()
サイトアイコン-2-150x150.png)