この記事について

タイトル画面の「ゲームを始める」を押して、別のシーンへ移動したい。ゲーム画面からタイトルにも戻りたい。そんなときは、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は普段は空欄ですが、切り替えに失敗した場合に理由を表示します。

手順1:Titleの子にContentを置き、タイトル文字と開始ボタンを並べます。
手順1:Titleの子にContentを置き、タイトル文字と開始ボタンを並べます。

手順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は別のものです。

手順2:game.tscnには回数表示・加算ボタン・戻るボタンを用意します。
手順2: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として操作しないでください。

手順3:開始ボタンのpressedから、保存済みのgame.tscnを指定します。
手順3:開始ボタンのpressedから、保存済みのgame.tscnを指定します。

手順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になったら「タイトルへ戻る」を押してください。

手順5:左はタイトル画面、右はゲーム画面で3回クリックした状態です。「タイトルへ戻る」で元の画面へ戻れます。
手順5:左はタイトル画面、右はゲーム画面で3回クリックした状態です。「タイトルへ戻る」で元の画面へ戻れます。

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

手順5:左は3回クリックしたゲーム画面、右はタイトルを経由して入り直したゲーム画面です。回数は0へ戻ります。
手順5:左は3回クリックしたゲーム画面、右はタイトルを経由して入り直したゲーム画面です。回数は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()