画面遷移を管理するスクリプトを書こう
グローバル変数
これまでとは異なることをします。res://singleton
フォルダーをまだ作っていない場合は、事前に作ってください。
まず、「ファイルシステム」ドックのres://singleton
フォルダーを右クリックして、「新規作成」>「スクリプト」を選びます。
「スクリプト作成」ダイアログが開きますので、「テンプレート」のチェックボックスを外し、「パス」をres://singleton/scene_manager.gd
にして「作成」ボタンを押します。
次にトップメニューの「プロジェクト」>「プロジェクト設定」を選び、「プロジェクト設定」ダイアログを開きます。そして「グローバル」タブ>「自動読み込み」タブを表示します。
「パス」の横にフォルダーのアイコンがあるのでクリックします。そして、作成したres://singleton/scene_manager.gd
を選択して、画面右端の「追加」ボタンを押します。そうすると、「名前」が「SceneManager」、「パス」が「res://singleton/scene_manager.gd
」という項目が追加されます。

この場所に登録されたスクリプトやシーンは、ゲームが起動した時に自動でインスタンス化されます。そして「名前」を利用して他のスクリプトから利用できます。
このグローバル変数は、通常のシーンツリーの外に配置されます。そして、シーンが遷移しても削除されず、どのシーンからでも利用可能になります。
シーンの遷移を管理するスクリプトは、シーンの作成や削除の影響を受けないようにしなければなりません。そのため、scene_manager.gd
をグローバル変数として登録して、「SceneManager」の名前で利用可能にします。
画面遷移を管理するスクリプト
scene_manager.gd
をダブルクリックして「Script」ビューで開きます。scene_manager.gd
に、次のようにシーン管理をおこなうプログラムを書きます。
extends Node const TITLE = "res://scn/title/title.tscn" const MAZE = "res://scn/maze/maze.tscn" const TRAN_LEAVE = preload("res://scn/transition/transition_leave.tscn") const TRAN_ENTER = preload("res://scn/transition/transition_enter.tscn") var first_enter = true # シーンのロード func load_scene(path) -> void: # 事前読み込み ResourceLoader.load_threaded_request(path) # シーン遷移演出 print("[load_scene] transition: start") var tran_leave = TRAN_LEAVE.instantiate() get_tree().get_current_scene().add_child(tran_leave) await tran_leave.leave() print("[load_scene] transition: end") # 読み込み完了するまで待機 while ResourceLoader.load_threaded_get_status(path) == \ ResourceLoader.THREAD_LOAD_IN_PROGRESS: print("[load_scene] wait load...") await get_tree().process_frame # シーン切り替え get_tree().change_scene_to_packed(ResourceLoader.load_threaded_get(path)) # シーン開始時のシーン遷移演出 func effect_enter_scene() -> void: # 初回は無視 if first_enter: first_enter = false return # シーン遷移演出 var tran_enter = TRAN_ENTER.instantiate() get_tree().get_current_scene().add_child(tran_enter) await tran_enter.enter()
以前のget_tree().change_scene_to_file("res://scn/maze/maze.tscn")
より、非常に長くなっています。シーン遷移の演出を加えたり、シーンを読み込むのを待機したり、さまざまな処理をおこなうようにしたためです。
スクリプトの解説1 定数と変数の定義
冒頭に、いくつかの定数や変数を用意しています。
const TITLE = "res://scn/title/title.tscn" const MAZE = "res://scn/maze/maze.tscn" const TRAN_LEAVE = preload("res://scn/transition/transition_leave.tscn") const TRAN_ENTER = preload("res://scn/transition/transition_enter.tscn") var first_enter = true
TITLE
とMAZE
は、今回のゲームに存在しているシーンのパスです。スクリプトの各所でパスを書くのは管理の都合上望ましくないです。そのためSceneManager.TITLE
のように、定数で利用可能にします。
TRAN_LEAVE
とTRAN_ENTER
は、先ほど作成した画面遷移の演出シーンです。
first_enter
は「ゲーム開始時だけ画面に入る演出をおこなわない」ためのフラグです。
スクリプトの解説2 load_scene関数
次はload_scene
関数です。ここでは「シーンの事前読み込みと切り替え」と「シーン遷移演出」の2種類の処理が入っています。
まずは、「シーン遷移演出」の方から解説します。
# シーン遷移演出 var tran_leave = TRAN_LEAVE.instantiate() get_tree().get_current_scene().add_child(tran_leave) await tran_leave.leave()
TRAN_LEAVE
をインスタンス化したあと、get_current_scene
関数で現在のシーンツリーのルートを得て、add_child
関数で追加します。そして、leave
関数を実行してawait
で演出が終わるのを待ちます。
このシーン遷移演出で画面をフェードアウトしたあと、裏でシーン遷移処理をおこないます。
次は「シーンの事前読み込みと切り替え」です。
# 事前読み込み ResourceLoader.load_threaded_request(path) # 読み込み完了するまで待機 while ResourceLoader.load_threaded_get_status(path) == \ ResourceLoader.THREAD_LOAD_IN_PROGRESS: await get_tree().process_frame # シーン切り替え get_tree().change_scene_to_packed(ResourceLoader.load_threaded_get(path))
ResourceLoader
を使って、読み込みを待ったあとにシーン切り替えをおこないます。
while
文のブロック内でawait get_tree().process_frame
を使うことで、読み込みが完了するまで待機します。
読み込み中かは、load_threaded_get_status
関数の戻り値がTHREAD_LOAD_IN_PROGRESS
なのかで分かります。
この方法でシーンを切り替える際は、以前出てきたchange_scene_to_file
関数ではなく、change_scene_to_packed
関数を利用します。
スクリプトの解説3 effect_enter_scene関数
続いて、effect_enter_scene
関数を説明します。
# シーン開始時のシーン遷移演出 func effect_enter_scene() -> void: # 初回は無視 if first_enter: first_enter = false return # シーン遷移演出 var tran_enter = TRAN_ENTER.instantiate() get_tree().get_current_scene().add_child(tran_enter) await tran_enter.enter()
この関数は、シーン開始時の遷移演出をおこないます。この関数は、遷移後のシーンの_ready
関数から呼び出します。
変数first_enter
を使い、最初の1回だけは処理をおこなわないようにします。
2回目以降は、TRAN_ENTER
をインスタンス化したあと、get_current_scene
関数で現在のシーンツリーのルートを得て、add_child
関数で追加します。そして、enter
関数を実行してawait
で演出が終わるのを待ちます。
この後、「Title」と「Maze」シーンにこの関数を書きます。その際は、await
を書かずにこの関数を実行します。待つべき処理がないためです。
そのため、effect_enter_scene
関数内のawait
はなくても構いません。今後、他のシーンを作る際に、演出が終わってから何かをしたいこともあるかもしれないのでawait
を付けています。
それでは次に、このシーン管理関数を、「Title」と「Maze」シーンに組み込んでいきましょう。