ステージで集めたコインが30点になった。ところが結果画面へ移ると、スコアは0に戻っている。画面を切り替える処理が動いても、数字まで自動で引き継がれるとは限りません。
UEでレベルを開き直すと、前のレベルで動いていたものは片付けられます。そのため、移る前にどこへ値を残し、移った後にどこから読むか を決めておく必要があります。
この記事では Open Level と GameInstance を使い、タイトル→ステージ→結果表示の一周を作ります。最初はキー操作で切り替え、スコアを持ち越せたら暗転を加えます。ボタンやコインの処理へ組み込む前に、レベルをつなぐ部分を小さく試してみましょう。
この記事でわかること
- Open Levelの2種類の指定方法
- GameInstanceへ数値を残す理由と、読み書きの方法
- タイトルで初期化し、結果画面で読み出す流れ
- 暗転と、パッケージ後の遷移を確認する手順
Open Levelは、次のレベルを開く
レベル遷移 は、今のレベルから別のレベルへ移ることです。タイトル用のレベルからステージ用のレベルへ移すなら、Open Levelの行き先にステージを指定します。
他のエンジンでいうと: Unity の
SceneManager.LoadScene、Godot のchange_scene_to_file()に当たります。
Open Levelの白い出力は、「次のレベルを読み終わった」という通知ではありません。後ろへつないだ処理を、移動先で実行するためのものとは考えないでください。
保存や後片付けはOpen Levelより前へ、移動先の準備は移動先のBeginPlayへ 分けます。たとえば、結果に表示するスコアは先に保存し、結果の表示処理はリザルト側で呼びます。BeginPlayは、そのレベルのプレイが始まるときの入口です。

Open Levelを呼んだ時点で、今のレベルは片付けに入ります。このグラフを持っているLevel BlueprintやActorも一緒に終わる ので、後ろにDelayやフェードを置いても最後まで走りません。やりたいことは、Open Levelより前に済ませてください。
名前で指定するか、一覧から選ぶか
Open Levelには2種類あります。この記事では、行き先が決まっているので by Object Reference を使います。
| ノード | 行き先の指定 | 気を付けること |
|---|---|---|
| Open Level (by Name) | Level Nameへ L_Stage01 などの名前を入力 | 名前の打ち間違い。移動先がビルドへ含まれるかも確認する |
| Open Level (by Object Reference) | Levelの一覧からレベルアセットを選択 | 選んだ相手を確認する。ビルドへの収録確認も行う |
参照 は「どのアセットを使うか」を指し示すものです。直接選ぶと、L_Stage01 を L_Stage1 と書いてしまうようなミスを減らせます。名前で切り替えたい場合はby Nameも使えますが、参照の有無だけでパッケージへの収録が必ず決まるわけではありません。

ほかの入力は、最初は「Absolute」をオン、「Options」を空欄にしておきます。Absoluteは以前の移動オプションを引き継ぐかの設定、Optionsは移動時に渡す追加情報です。今回のスコアは、次に説明するGameInstanceへ置きます。
何が作り直され、何が残る?
通常のOpen Levelでは、前のレベルにいたActorを次のレベルへそのまま運ぶことはできません。Actorは、プレイヤーやコインなど、レベルで動くものです。
| データの置き場所 | 通常のOpen Levelで移った後 |
|---|---|
| コインやプレイヤーなどのActor | 前の実体は終了する。次のレベルでは必要なものを作り直す |
| Level Blueprintの変数 | 前のレベルの値は引き継がれない |
| GameMode / GameState / PlayerState | 作り直される。値を残すなら別の置き場へ渡す |
| GameInstanceの数値や文字列 | 同じゲームを起動している間は残る |
| ディスクへ保存したセーブファイル | ゲームを終了した後も残る |
GameInstance は、ゲームを起動してから終了するまで使う入れ物です。レベルを読み替えても同じものが残るので、今回のスコアの 置き場に向いています。GameInstanceと同じ期間使えるSubsystemなどもありますが、まずはこの入れ物1つで試せます。
他のエンジンでいうと: Unity で
DontDestroyOnLoadを付けた管理用オブジェクト、Godot の Autoload(シングルトン) に当たります。Unityのように重複生成を自分で防ぐ必要はありませんが、 Project Settingsに登録して初めて使える ところは、GodotのAutoloadと同じです。
注意したいのは、GameInstanceへActorの参照を保存しても、そのActorが生き残るわけではない ことです。コインそのものを覚えるのではなく、「得点30」「アイテムの種類と個数」のようなデータを残します。

UIのWidgetも、前のレベルの状態を持ち越す入れ物にはしません。寿命は保持方法にもよるため、遷移前に不要なWidgetを Remove from Parent で外し、移った先で必要なUIを作ると整理しやすくなります。
ここで扱うのは1人用ゲームの通常のOpen Levelです。ネットワークでのSeamless Travelや、同じ世界へ一部のレベルを追加するLevel Streamingとは、残るものや手順が異なります。
GameInstanceにスコアの置き場を作る
まず、自分のゲーム用のGameInstanceを用意します。
- コンテンツブラウザで「Blueprint Class → All Classes」を開き、
GameInstanceを親にして作ります。 BP_GameInstanceと名付け、Integer型の変数TotalScoreを追加します。Integerは整数を扱う型です。- Compileして初期値を
0にし、保存します。 - 「Project Settings → Maps & Modes → Game Instance Class」に
BP_GameInstanceを指定します。

作成と登録は別の作業です。 4番目を忘れると、ゲームでは既定のGameInstanceが使われ、自分が追加したTotalScoreを読めません。
プレイ中に取得するときは Get Game Instance を使い、そのReturn Valueを Cast To BP_GameInstance のObjectへつなぎます。Castは、取得した相手が自分のクラスか確かめる操作です。成功すると「As BP Game Instance」から、TotalScoreを持つ相手を指定できます。
GameInstance自体が存在していても、登録したクラスが違えば、このCastは失敗します。作成済みだから必ず成功する、とは考えないようにしましょう。
ゲームを終了したり、エディタのPlayを止めたりすると、この入れ物も終了します。次回起動まで残すには セーブ/ロード が必要です。置き場所の考え方を詳しく知りたい場合は GameInstanceの記事 へ進めます。
実践:3つのレベルをスコア付きでつなぐ
Third Personテンプレートを使います。最初はボタンやコインを作らず、Enterで開始、Kで10点加算、Gで結果表示 にします。3回加算して結果へ移り、SCORE: 30 が出たら成功です。

① レベルを3つ用意する
プレイヤーが動くテンプレートのレベルを「File → Save Current Level As」で別名保存し、Content/Maps に次の3つを作ります。床・Player Start・Third Person用GameModeの設定を引き継いでおきます。
| レベル | 今回の役割 |
|---|---|
L_Title | スコアを0に戻して開始する |
L_Stage01 | スコアを加算する |
L_Result | 持ち越したスコアを表示し、タイトルへ戻る |
今回は同じ風景でも構いません。操作するプレイヤーも各レベルに出ます。名前が似ているので、編集前にどのレベルを開いているか確かめてください。Play設定は「Number of Players: 1」「Net Mode: Play Standalone」にします。
各レベルの「Blueprints → Open Level Blueprint」を開き、BeginPlayからPrint Stringで L_Title、L_Stage01、L_Result とそれぞれ表示すると、移動先を見分けやすくなります。Print Stringは画面・ログ表示をオン、Durationを 10.0 にします(→ ログの使い方)。
② まずは切り替えるだけの一周を作る
各Level Blueprintにキーボードイベントを置き、Pressedの白い出力からOpen Levelへつなぎます。by Object ReferenceのLevel欄で、次の行き先を選びます。
| 配線するレベル | キー | 行き先 |
|---|---|---|
| L_Title | Enter | L_Stage01 |
| L_Stage01 | G | L_Result |
| L_Result | Enter | L_Title |
Compile・Saveし、L_TitleからPlayします。ゲーム画面をクリックして入力を受け取れる状態にし、Enter→G→Enterの順に押してください。Output Logへレベル名が順に出れば、一周できています。結果から戻る経路も、ここで用意しておきます。
③ タイトルでスコアを0にする
Playを止め、L_TitleのBeginPlay側にGameInstanceを取得する処理を足します。
- BeginPlayの白い実行線を
Cast To BP_GameInstanceへつなぎます。 Get Game InstanceのReturn ValueをCastのObjectへつなぎます。- Castの「As BP Game Instance」からドラッグして
Set TotalScoreを置きます。SetのTargetへ、Castの結果をつなぎます。 - Cast成功側の白い出力からSetへつなぎ、TotalScoreの入力値を
0にします。その後ろへ、先ほどのレベル名のPrint Stringをつなぎます。
Setは、指定した相手の変数へ値を入れる操作 です。ここではLevel Blueprint自身ではなく、取得したGameInstanceのTotalScoreを0にします。
この初期化を結果側に置くと、表示したかった得点を自分で消してしまいます。新しい一周を始めるタイトル側で0へ戻すと、再挑戦の区切りが明確になります。
④ 10点を加える処理を作る
BP_GameInstanceを開き、「My Blueprint → Functions」の+から AddTen という関数を作ります。関数は、処理をひとまとまりにして名前を付け、別の場所から呼べるようにしたものです。今回は入力や戻り値を追加せず、10点を足す処理だけをまとめます。

- TotalScoreをグラフへドラッグして「Get」を選び、現在値を読みます。
- その値を整数の
+ノードへつなぎ、もう片方の入力に10を入れます。 - TotalScoreをもう一度ドラッグし、今度は「Set」を置きます。足し算の結果をSetの数値入力へつなぎます。
- 関数の開始ノードAddTenから、Setの白い実行入力へつなぎます。
このグラフはGameInstanceの中なので、GetとSetは Self、つまり自分自 身 のTotalScoreを読み書きします。Compile・Saveしてください。
次にL_Stage01のLevel Blueprintへ戻り、KのPressed→Cast To BP_GameInstance→Add Tenという順で呼びます。CastのObjectにはGet Game InstanceのReturn Value、Add TenのTargetにはCastの「As BP Game Instance」をつなぎます。

Get Game Instanceは相手を取得するだけなので、白い実行線はありません。白い線は処理の順番、青い線は処理する相手 を表しています。Get Game InstanceからCastのObjectへ渡し、成功した結果をAdd TenのTargetへつなぎます。
⑤ 結果画面で読む
L_ResultのBeginPlayから、同じようにGet Game InstanceとCastを使って相手を取得します。今度はTotalScoreを変更せず、Getで読みます。
- Castの「As BP Game Instance」から
Get TotalScoreを置きます。 - 値の出力から
To String (Integer)を作り、整数を文字へ変えます。 - 文字をつなぐ
Appendノードを置き、AにSCORE:、Bに変換した文字を渡します。 - AppendのReturn ValueをPrint StringのIn Stringへつなぎます。白い実行線は、Cast成功側からPrint Stringへつなぎます。
Print StringのDurationは10秒、画面・ログ表示はオンにします。元のレベル名のPrint Stringをこの結果表示へ置き換えても構いません。Enterでタイトルへ戻る配線は、そのまま残します。
ここでは「画面の表示」 も確認用のPrint Stringです。製品のリザルト画面は、同じ値を UMGのText へ渡して作れます。
⑥ 加算回数を変えて確かめる
すべてCompile・Saveしたら、L_TitleからPlayし直します。途中でエディタの停止を押すとGameInstanceも終了するため、結果へは必ずゲーム内のGキーで移ってください。
| 操作 | 結果画面の表示 |
|---|---|
| Enter → Kを3回 → G | SCORE: 30 |
| Enter → Kを2回 → G | SCORE: 20 |
| Enter → Kを押さずG | SCORE: 0 |
| 結果からEnterでタイトルへ戻り、もう一周 | 前の点数へ加算されず、0から始まる |
最後に、L_Titleの Set TotalScore = 0 への接続を一時的に外し、BeginPlayからレベル名のPrint Stringだけを呼ぶ形で試してみましょう。Playを続けたまま一周すると、今度は前の点数が残ります。残ること自体がGameInstanceの働きで、いつ0へ戻すかは自分で決める と分かります。確認後は初期化の接続を戻します。
暗転してから移る
スコアを持ち越せたら、ステージから結果へ移る直前に暗転を加えます。画面が突然切り替わるのを和らげるための演出です。
ステージ側:暗転を待ってからOpen Level
L_Stage01でGから直接Open Levelへつないでいた線を、次の順へ変更します。
GのPressed → Do Once → Start Camera Fade → Delay → Open Level
Do Once は、最初の1回だけ後ろへ処理を通すノードです。「Start Closed」はオフ、Resetは未接続にします。暗転中にGを連打しても、同じ遷移を繰り返し始めないために使います。
Start Camera FadeのTargetへ、Get Player Camera Manager のReturn Valueをつなぎます。Player Indexは 0、今回の1人のプレイヤーです。Player Camera Managerは、そのプレイヤーのカメラ映像を管理する相手です。
| Start Camera Fadeの設定 | 値 |
|---|---|
| From Alpha | 0.0 |
| To Alpha | 1.0 |
| Duration | 0.5 |
| Color | 黒 |
| Should Fade Audio | オン |
| Hold when Finished | オン |
Alphaは色の不透明度 です。0なら元の映像が見え、1なら指定した色で覆われます。ここでは0.5秒かけて黒くします。Should Fade Audioは音量も一緒に下げる設定、Hold when Finishedは終わった後も黒を保つ設定です。
Start Camera Fadeは、暗転を始めるとすぐ次のノードへ進みます。そこでDelayを 0.6 秒にし、暗転の0.5秒より少し長く待ちます。そのCompletedから、L_Resultを指定したOpen Levelへつなぎます。Delayは、この後ろの処理へ進むのを待たせるノードで、ゲーム全体を止める処理 ではありません。

図はStart Camera Fade以降の部分です。左端の白い実行入力へ、Do OnceのCompletedをつなぎます。
結果側:明るく戻す
L_ResultのBeginPlayとGameInstanceのCastの間へ、Start Camera Fadeを追加します。Targetには、このレベルのGet Player Camera Manager(Index 0)を渡します。
From Alphaを 1.0、To Alphaを 0.0、Durationを 0.5、Colorを黒にし、Should Fade Audioはオン、Hold when Finishedはオフにします。その白い出力から、既に作ったスコアの取得・表示へ進めます。
これで、Gを押すと暗くなってからレベルが変わり、結果側で明るく戻ります。読み込みが長いほど暗い時間も長くなります。暗転は読み込みを短縮するものではないので、長い待ち時間が気になる場合は読み込み側も見直します。
カメラの暗転は、通常のUMG表示全体を覆うものではありません。 タイトルやHUDごと暗くしたいときは、全画面の黒いImageを持つWidgetで覆う方法を使います。今回のPrint Stringも暗転の確認用UIではないため、文字の見え方よりカメラ映像の変化を確かめてください。
ロード中のWidgetが止まる理由
Widgetは、文字やボタンなどのUIをまとめる部品です。ロード中に動くアイコンを出したくなったとき、Create Widget → Add to Viewport → Open Level と並べるだけでは、期待どおりに動き続けるとは限りません。
通常のレベル読み込みには、読み終わるまでゲーム側の処理を待たせる、同期的な処理 が入ります。その間、普通のWidgetを動かす処理も止まるため、描画される前に読み込みへ入ったり、表示されたまま動きが止まったりします。

Open Levelの前にDelayを挟むと、Widgetを先に表示する時間は作れます。ただし、それだけで読み込み中も動くようになるわけではありません。短い切り替えなら、先に暗転を済ませる方法から始められます。
動くロード画面が必要なら、MoviePlayerなどの専用機構や、読み込み中も今の世界を動かせる構成を検討します。「軽いロード用レベルを挟み、そこからもう一度Open Levelする」だけでは、次の読み込みで同じ待ちが発生します。
パッケージ後にも一周できるか確認する
エディタで開けたマップでも、配布用のゲームへ入っていなければ遷移できません。パッケージ化 は、必要なアセットを実行環境向けに変換し、配布できる形へまとめる工程です。
- 「Project Settings → Packaging」で「List of maps to include in a packaged build」を探します。
- L_Title、L_Stage01、L_Resultの3つを登録します。
- 「Maps & Modes → Game Default Map」をL_Titleへ設 定します。
- パッケージ化したゲームでも、タイトル→ステージ→結果→タイトルと一周します。

変換してゲームへ含める処理は Cook と呼ばれます。独自のビルド手順では別のマップ一覧や除外設定が使われることもあるので、最後は実際の配布物で確かめます。Print Stringで今回の結果を確認する段階はDevelopmentビルドを使い、製品向けのShippingではUMGへ結果表示を移します(→ パッケージ化の記事)。
おまけ:画面遷移をゲームへ組み込む
うまく動かないとき
| 症状 | 確認する場所 |
|---|---|
| GameInstanceのCastが失敗する | Maps & ModesのGame Instance Class。Cast FailedからPrint Stringで確認できる |
| 結果がいつも0 | AddTenが呼ばれたか、結果側で初期化していないか、途中でPlayを止めていないか |
| 初期化しても次の一周に点数が残る | L_Titleで取得したGameInstanceをSet TotalScoreのTargetへ渡しているか |
| 暗転が途中で切れる | FadeのDurationよりDelayが短くないか、Hold when Finishedがオンか |
| 真っ暗なまま次へ進まない | 行き先の指定と収録、Output Logの読み込みエラー、結果側のフェードイン |
| キーで移れない | 開いているレベルのグラフか、ゲーム画面に入力が届いているか、白い実行線がつながっているか |
| 移った先だけキーが効かない | そのレベルのGameModeが別のものになっていないか。テンプレートのGameModeを引き継げていないと、操作の受け口ごと変わる |
| 移った先で床下や妙な位置に出る | 移動先のレベルにPlayer Startがあるか。無いと原点に出るため、床の外に落ちることがある |
キーをボタンやゴールへ置き換える
一周が動いたら、タイトルのEnterをボタンのOnClicked、ステージのKをコインの取得、Gをゴールへの接触へ置き換えられます。処理の入口が変わっても、「加点→結果へ移る→保存した値を読む」という流れは同じです。コインやゴールの作り方は コイン集めゲーム で扱います。
遷移を複数の場所から呼ぶようになったら、連打防止も含めて1つの処理へまとめます。Level Blueprintはそのレベル専用なので、共通化したい処理はGameModeなどへ移すと使い回しやすくなります(→ Level BlueprintとBlueprint Class)。
Open LevelとLevel Streamingの違い
Open Levelは世界を読み替えます。Level Streaming は、同じ世界の中へ一部のレベルを追加したり、不要な部分を外したりする仕組みです。
タイトルから本編へ切り替えるならOpen Levelが使いやすく、隣の部屋へ地続き に歩かせたいならLevel Streamingなどを検討します。Streamingでも読み込んだ部分を外せば、そこに属するActorは終了するので、何でも残るわけではありません(→ Level Streamingの記事)。
同じステージを最初からやり直す場合もOpen Levelを使えます。ただしGameInstanceの得点は残るため、リトライ時の初期化は別途決めます。ステージを読み直さず復帰地点へ戻したい場合は チェックポイントとリスポーン が向いています。
まとめ
Open Levelでレベルを移るときは、スコアなどの数値をGameInstanceへ残し、移動先で読みます。残した値をいつ0へ戻すかも、ゲームの一周に合わせて決めておきましょう。
まずはキー操作で、切り替え・持ち越し・再挑戦を確かめます。そこへ暗転とUIを加え、最後にパッケージ版でも一周できれば、ゲーム全体をつなぐ土台ができます。