画面遷移を管理するスクリプトを書こう
グローバル変数
これまでとは異なることをします。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」シーンに組み込んでいきましょう。
