タイトル画面でボタンを押したらステージが始まり、ゴールに触れたらリザルトが出る。ゲームを1本仕上げるには、この骨格が必ず要ります。切り替えるノード自体は1つだけで、置いてつなげば動きます。
つまずくのはその後です。ステージで集めたコインの数がリザルトで0になり、ロード中は画面が固まり、パッケージした途端 に遷移が失敗します。この記事では、 Open Level の2種類の使い分け、レベルを移ると何が消えて何が残るのか、そしてロード画面をどう扱うかを解説します。
この記事でわかること
- Open Level (by Name) と (by Object Reference) の違いと選び方
- レベルを移ると ほぼ全部消える という前提
- 唯一生き残る GameInstance にスコアを預ける
- ロード画面のWidgetが 表示されない理由 と現実的な回避策
- Level Streaming とはどう違うのか
- 実践: タイトル → ステージ → リザルトをスコア付きでつなぐ
切り替えるノードは1つだけ
レベルの切り替えは Open Level ノード1つで完了します。ボタンのクリックでも、ゴールへの接触でも、呼び出すのはこれだけです。
ただし、動きを1つだけ覚えておいてください。 Open Level を呼んでも、その場で切り替わるわけではありません。 移動の予約が入り、実際の処理はそのフレームの終わりに走ります。
そのため、Open Levelの後ろにノードをつないでも、それは 今のレベルが消える直前に実行される ことになります。

Open Level (by Object Reference)
→ Set Score = 0 ← 今のレベルで実行され、直後に消える。意味がない
スコアの保存や後始末は、必ず Open Level を呼ぶ前 に済ませてください。この順序を逆にしているのが、「保 存したはずのデータが無い」の典型的な原因です。
by Name と by Object Reference
UE5では、Open Levelが2つに分かれています。

| ノード | 指定の仕方 | 特徴 |
|---|---|---|
| Open Level (by Name) | Level Name に名前(Name型)を書く | 名前を後から組み立てられる。綴り間違いに気付けない |
| Open Level (by Object Reference) | Level にレベルアセットを直接指定する | 一覧から選ぶので間違えない。名前変更にも追従する |
基本は Open Level (by Object Reference) を使ってください。 理由は2つあります。
- 綴りを間違えられない: ドロップダウンから選ぶだけなので、
L_Stage01をL_Stage1と書く事故が起きません - パッケージに確実に含まれる: アセットへの参照が残るため、ビルド時にそのマップも一緒に取り込まれます
2つ目が特に重要です。 by Name は文字列でしかないので、ビルド時に「このマップが必要だ」と判断できません。 エディタでは動くのに、パッケージした 実行ファイルだけ遷移に失敗する、という原因の分かりにくい不具合になります。
by Nameを使わざるを得ないのは、L_Stage01 L_Stage02 のように 名前を動的に組み立てるとき です。その場合は、対象のマップを Project Settings > Project > Packaging の List of maps to include in a packaged build へ手動で追加してください。詳しくはパッケージングの記事で扱っています。
| ピン | 内容 |
|---|---|
| Absolute | 既定のオンのままで問題ありません |
| Options | ?Difficulty=Hard のような追加情報。GameMode側で受け取る形になり扱いはやや面倒なので、値の持ち回りは後述のGameInstanceの方が簡単です |
なお、レベル名を間違えたときは画面が真っ暗になったり、その場に留まったりします。この状態になったら、まず Output Log を開いて読み込み失敗の警告が出ていないかを確認してください(→ ログの記事)。
レベルを移ると、ほぼ全部消える
ここが最大の落とし穴です。Open Levelを実行すると、 今のレベルにあるものは基本的にすべて破棄されます。

| 消えるもの | 残るもの |
|---|---|
| レベルに置いたActorすべて | GameInstance |
| プレイヤーのCharacter | GameInstance Subsystem |
| 画面に出したWidget | セーブファイル(ディスク上) |
| GameMode / GameState / PlayerState | |
| Level Blueprintの変数 |
GameMode や PlayerState まで作り直しになる点に注意してください。これらは レベルごとに用意される ものだからです。World Settingsの GameMode Override でレベルごとに別のGameModeを指定できるのも、この仕組みがあるためです(→ ゲームフレームワークの記事)。
「PlayerStateにスコアを持たせていたのに、リザルトで0になった」という詰まり方をよくしますが、仕様どおりの挙動です。PlayerStateは1つのレベルの中で完結する入れ物です。
GameInstanceに預ける
セッションが続く限り生き残る唯一の入れ物が GameInstance です。ゲームを起動してから終了するまで、1 つだけ存在し続けます。
作り方は3手順です。
- コンテンツブラウザで Blueprint Class を作り、親クラスに GameInstance を選ぶ(
ALL CLASSESから検索します) BP_GameInstanceと名付け、持ち越したい変数を追加する- Project Settings > Project > Maps & Modes > Game Instance Class で、いま作ったクラスを指定する
3番目を忘れると何も起きません。 「GameInstanceを作ったのに値が保持されない」ときは、まずここを確認してください。
読み書きはどこからでもできます。

Get Game Instance
→ Cast To BP_GameInstance
→ Set TotalScore = TotalScore + 10
Get Game Instance はどのBlueprintからでも呼べます。Castが1回入りますが、GameInstanceは常に存在するので失敗しません。
補足: Castを毎回書きたくない場合や、機能ごとにファイルを分けたい場合は GameInstance Subsystem が使えます。GameInstanceと同じ寿命を持ちながら、クラスを肥大化させずに済みます(→ Subsystemの記事)。
なお、GameInstanceが残るのは アプリを起動している間だけ です 。ゲームを終了すれば消えます。次回起動時にも残したいデータは、セーブ/ロードの仕組みでディスクへ書き出してください。
ロード画面が表示されない理由
ロード中に「Now Loading」を出したくなります。素直に考えると、こう組むはずです。
Create Widget (WBP_Loading) → Add to Viewport → Open Level
これは 動きません。 Widgetは確かに作られますが、画面には一度も映りません。
理由は、レベルの読み込みが 同期処理 だからです。

画面に何かが映るには、そのフレームが描画される必要があります。ところがOpen Levelから始まる読み込みの間、ゲームスレッドは読み込みに専念していて フレームが1枚も描かれません 。追加したWidgetは、描画される機会がないまま、新しいレベルへの切り替えで破棄されます。
結果として、プレイヤーには 数秒間フリーズした画面 だけが見えます。「ロード中に固まる」という感想の正体はこれです。
本格的なタイトルは、ここを非同期の読み込みと専用のロード画面機構で解決しています。ただし個人開発でそこまで作り込むのは負担が大きく、割に合わないことが多いです。
現実的な落とし所:先に暗転させる
フリーズ自体は消せません。 フリーズしていることを目立たなくする のが現実的な解決策です。
やることは単純で、 画面を完全に暗転させてから Open Level を呼びます。 暗転が終わった状態で固まるので、プレイヤーには「暗転が少し長い」としか見えません。

遷移させたいタイミング
→ Get Player Camera Manager (Player Index = 0)
→ Start Camera Fade
From Alpha = 0.0
To Alpha = 1.0
Duration = 0.5
Color = Black
Should Fade Audio = true
Hold When Finished = true
→ Delay (0.5) ← 暗転が終わるまで待つ
→ Open Level (by Object Reference)
Hold When Finished をオンにしておくのが要点です。オフだと暗転が終わった瞬間に元へ戻り、フリーズした画面が見えてしまいます。
Should Fade Audio をオンにすると音も一緒にフェードします。画面だけ暗くなって足音が鳴り続けると違和感があるので、基本はオンにしてください。
遷移先のレベルでは、逆向きのフェードで明るく戻します。
Event BeginPlay
→ Get Player Camera Manager
→ Start Camera Fade (From Alpha = 1.0, To Alpha = 0.0, Duration = 0.5, Color = Black)
注意: Start Camera Fade はUMGのWidgetを覆いません。 カメラの映像に対する処理なので、UIは暗転の上に残ります。HUDごと隠したい場合は、全画面の黒いImageを持つWidgetを作り、そのアニメーションで不透明度を上げる方法にしてください(→ UMGの記事)。
実践:タイトルからリザルトまでつなぐ
パズルゲームのステージ選択、ローグライトの1周、レースゲームの1レース。 タイトル → 本編 → 結果表示 という3枚の骨格は、ジャンルを問わず同じ形になります。ここではスコアを持ち越しながら、その一周を作ります。
完成形
タイトルのボタンでステージが始まり、コインを3枚拾ってゴールに触れると、リザルト画面に SCORE: 30 と表示されます。

再現条件
Third Person テンプレート で新規プロジェクトを作り、次を用意します。
レベル3つ(File > New Level > Basic で作り、Content/Maps に保存)
| レベル名 | 中身 |
|---|---|
L_Title | Player Startのみ |
L_Stage01 | Player Start、BP_Coin を3つ、BP_Goal を1つ。World Settingsの GameMode Override を BP_ThirdPersonGameMode に |
L_Result | Player Startのみ |
GameInstance
| クラス | 変数名 | 型 | 既定値 |
|---|---|---|---|
BP_GameInstance(親: GameInstance) | TotalScore | Integer | 0 |
作成後、 Project Settings > Project > Maps & Modes > Game Instance Class を BP_GameInstance に設定します。
Actor 2つ(親クラスはどちらもActor)
| クラス | コンポーネント | 設定 |
|---|---|---|
BP_Coin | Sphere Collision(Radius 50.0 )+ Static Mesh(Sphere、Scale 0.5 ) | Collision Presetは OverlapAllDynamic |
BP_Goal | Box Collision(Box Extent 100 / 100 / 100 ) | 同上 |
BP_Goal には bool 型の変数 IsTransitioning (既定値 false )も追加します。ゴールに何度も触れて遷移が二重に走るのを防ぐためです。
タイトルからステージへ
L_Title のLevel Blueprintに組みます(UIは省き、キー入力で始めます)。
Event BeginPlay
→ Get Game Instance → Cast To BP_GameInstance
→ Set TotalScore = 0 ← 2周目のためにここで初期化する
Space Bar (Pressed)
→ Open Level (by Object Reference) Level = L_Stage01
スコアの初期化をタイトルで行うのが要点です。リザルト側で0に戻すと、リザルト画面を開いた瞬間に自分の表示するスコアを消してしまいます。
コインとゴール
BP_Coin のイベントグラフです。
Event ActorBeginOverlap (Other Actor)
→ Cast To BP_ThirdPersonCharacter (Other Actor) ← プレイヤー以外で反応させない
→ Get Game Instance → Cast To BP_GameInstance
→ Set TotalScore = TotalScore + 10
→ Print String (TotalScore) ← 確認用
→ Destroy Actor
BP_Goal は、暗転を挟んでからレベルを切り替えます。
