【Unreal Engine】UE5のレベル遷移入門:Open Levelとロード画面、消えるデータの受け皿

作成: 2026-07-20

タイトルからステージ、そしてリザルトへ。Open Level (by Name)と(by Object Reference)の違い、レベルを移ると何が消えて何が残るのか、GameInstanceでのスコアの持ち越し、そして「ロード画面が表示されない」問題の現実的な回避策まで。

タイトル画面でボタンを押したらステージが始まり、ゴールに触れたらリザルトが出る。ゲームを1本仕上げるには、この骨格が必ず要ります。切り替えるノード自体は1つだけで、置いてつなげば動きます。

つまずくのはその後です。ステージで集めたコインの数がリザルトで0になり、ロード中は画面が固まり、パッケージした途端に遷移が失敗します。この記事では、 Open Level の2種類の使い分け、レベルを移ると何が消えて何が残るのか、そしてロード画面をどう扱うかを解説します。

タイトル・ステージ・リザルトの3つの画面が矢印でつながり、スコアだけが別の経路で運ばれる図

この記事でわかること

  • Open Level (by Name)(by Object Reference) の違いと選び方
  • レベルを移ると ほぼ全部消える という前提
  • 唯一生き残る GameInstance にスコアを預ける
  • ロード画面のWidgetが 表示されない理由 と現実的な回避策
  • Level Streaming とはどう違うのか
  • 実践: タイトル → ステージ → リザルトをスコア付きでつなぐ

Sponsored

切り替えるノードは1つだけ

レベルの切り替えは Open Level ノード1つで完了します。ボタンのクリックでも、ゴールへの接触でも、呼び出すのはこれだけです。

ただし、動きを1つだけ覚えておいてください。 Open Level を呼んでも、その場で切り替わるわけではありません。 移動の予約が入り、実際の処理はそのフレームの終わりに走ります。

そのため、Open Levelの後ろにノードをつないでも、それは 今のレベルが消える直前に実行される ことになります。

1フレームの帯の途中でOpen Levelが呼ばれ、その後ろのSet Scoreは消える直前のレベルで実行され、フレームの終わりにレベル破棄と新レベル読み込みが走ることを示したタイムライン図
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つあります。

  1. 綴りを間違えられない: ドロップダウンから選ぶだけなので、L_Stage01L_Stage1 と書く事故が起きません
  2. パッケージに確実に含まれる: アセットへの参照が残るため、ビルド時にそのマップも一緒に取り込まれます

2つ目が特に重要です。 by Name は文字列でしかないので、ビルド時に「このマップが必要だ」と判断できません。 エディタでは動くのに、パッケージした実行ファイルだけ遷移に失敗する、という原因の分かりにくい不具合になります。

by Nameを使わざるを得ないのは、L_Stage01 L_Stage02 のように 名前を動的に組み立てるとき です。その場合は、対象のマップを Project Settings > Project > PackagingList of maps to include in a packaged build へ手動で追加してください。詳しくはパッケージングの記事で扱っています。

ピン内容
Absolute既定のオンのままで問題ありません
Options?Difficulty=Hard のような追加情報。GameMode側で受け取る形になり扱いはやや面倒なので、値の持ち回りは後述のGameInstanceの方が簡単です

なお、レベル名を間違えたときは画面が真っ暗になったり、その場に留まったりします。この状態になったら、まず Output Log を開いて読み込み失敗の警告が出ていないかを確認してください(→ ログの記事)。

Sponsored

レベルを移ると、ほぼ全部消える

ここが最大の落とし穴です。Open Levelを実行すると、 今のレベルにあるものは基本的にすべて破棄されます。

左側にActor・Widget・GameMode・PlayerStateなどが消える様子、右側にGameInstanceだけが残る様子を対比した図
消えるもの残るもの
レベルに置いたActorすべてGameInstance
プレイヤーのCharacterGameInstance Subsystem
画面に出したWidgetセーブファイル(ディスク上)
GameMode / GameState / PlayerState
Level Blueprintの変数

GameModePlayerState まで作り直しになる点に注意してください。これらは レベルごとに用意される ものだからです。World Settingsの GameMode Override でレベルごとに別のGameModeを指定できるのも、この仕組みがあるためです(→ ゲームフレームワークの記事)。

「PlayerStateにスコアを持たせていたのに、リザルトで0になった」という詰まり方をよくしますが、仕様どおりの挙動です。PlayerStateは1つのレベルの中で完結する入れ物です。

GameInstanceに預ける

セッションが続く限り生き残る唯一の入れ物が GameInstance です。ゲームを起動してから終了するまで、1つだけ存在し続けます。

作り方は3手順です。

  1. コンテンツブラウザで Blueprint Class を作り、親クラスに GameInstance を選ぶ(ALL CLASSES から検索します)
  2. BP_GameInstance と名付け、持ち越したい変数を追加する
  3. Project Settings > Project > Maps & Modes > Game Instance Class で、いま作ったクラスを指定する

3番目を忘れると何も起きません。 「GameInstanceを作ったのに値が保持されない」ときは、まずここを確認してください。

読み書きはどこからでもできます。

ステージでコインを取るたびGameInstanceのTotalScoreが増え、レベルが切り替わってもその値が残ってリザルトで表示される流れ図
Get Game Instance
  → Cast To BP_GameInstance
  → Set TotalScore = TotalScore + 10

Get Game Instance はどのBlueprintからでも呼べます。Castが1回入りますが、GameInstanceは常に存在するので失敗しません。

補足: Castを毎回書きたくない場合や、機能ごとにファイルを分けたい場合は GameInstance Subsystem が使えます。GameInstanceと同じ寿命を持ちながら、クラスを肥大化させずに済みます(→ Subsystemの記事)。

なお、GameInstanceが残るのは アプリを起動している間だけ です。ゲームを終了すれば消えます。次回起動時にも残したいデータは、セーブ/ロードの仕組みでディスクへ書き出してください。

Sponsored

ロード画面が表示されない理由

ロード中に「Now Loading」を出したくなります。素直に考えると、こう組むはずです。

Create Widget (WBP_Loading) → Add to Viewport → Open Level

これは 動きません。 Widgetは確かに作られますが、画面には一度も映りません。

理由は、レベルの読み込みが 同期処理 だからです。

Widgetを追加した直後にロードが始まり、その間フレームが1枚も描かれないまま次のレベルへ切り替わるタイムライン図

画面に何かが映るには、そのフレームが描画される必要があります。ところがOpen Levelから始まる読み込みの間、ゲームスレッドは読み込みに専念していて フレームが1枚も描かれません 。追加したWidgetは、描画される機会がないまま、新しいレベルへの切り替えで破棄されます。

結果として、プレイヤーには 数秒間フリーズした画面 だけが見えます。「ロード中に固まる」という感想の正体はこれです。

本格的なタイトルは、ここを非同期の読み込みと専用のロード画面機構で解決しています。ただし個人開発でそこまで作り込むのは負担が大きく、割に合わないことが多いです。

現実的な落とし所:先に暗転させる

フリーズ自体は消せません。 フリーズしていることを目立たなくする のが現実的な解決策です。

やることは単純で、 画面を完全に暗転させてから Open Level を呼びます。 暗転が終わった状態で固まるので、プレイヤーには「暗転が少し長い」としか見えません。

Start Camera Fadeで暗転し、Delayで暗転の完了を待ち、真っ暗になってからOpen Levelを呼ぶ手順を3段で示した図
遷移させたいタイミング
  → 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の記事)。

Sponsored

実践:タイトルからリザルトまでつなぐ

パズルゲームのステージ選択、ローグライトの1周、レースゲームの1レース。 タイトル → 本編 → 結果表示 という3枚の骨格は、ジャンルを問わず同じ形になります。ここではスコアを持ち越しながら、その一周を作ります。

完成形

タイトルのボタンでステージが始まり、コインを3枚拾ってゴールに触れると、リザルト画面に SCORE: 30 と表示されます。

タイトルのボタンからステージへ、コイン3枚でTotalScoreが30になり、ゴールからリザルトへ運ばれてSCORE:30と表示される流れ図

再現条件

Third Person テンプレート で新規プロジェクトを作り、次を用意します。

レベル3つFile > New Level > Basic で作り、Content/Maps に保存)

レベル名中身
L_TitlePlayer Startのみ
L_Stage01Player Start、BP_Coin を3つ、BP_Goal を1つ。World Settingsの GameMode OverrideBP_ThirdPersonGameMode
L_ResultPlayer Startのみ

GameInstance

クラス変数名既定値
BP_GameInstance(親: GameInstance)TotalScoreInteger0

作成後、 Project Settings > Project > Maps & Modes > Game Instance ClassBP_GameInstance に設定します。

Actor 2つ(親クラスはどちらもActor)

クラスコンポーネント設定
BP_CoinSphere Collision(Radius 50.0 )+ Static Mesh(Sphere、Scale 0.5Collision Presetは OverlapAllDynamic
BP_GoalBox 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 は、暗転を挟んでからレベルを切り替えます。

BP_Goalのオーバーラップから二重防止のBranch、カメラフェード、Delay、Open Level��へとつながるノードグラフ
Event ActorBeginOverlap (Other Actor)
  → Cast To BP_ThirdPersonCharacter (Other Actor)
  → Branch (Condition = IsTransitioning)
      True  → (何もしない)
      False ↓
  → Set IsTransitioning = true
  → Get Player Camera Manager (0)
  → Start Camera Fade (From 0.0 → To 1.0 / Duration 0.5 / Black / Fade Audio / Hold When Finished)
  → Delay (0.5)
  → Open Level (by Object Reference)  Level = L_Result

リザルトで表示する

L_Result のLevel Blueprintで、持ち越した値を読みます。

Event BeginPlay
  → Get Player Camera Manager (0)
  → Start Camera Fade (From 1.0 → To 0.0 / Duration 0.5 / Black)
  → Get Game Instance → Cast To BP_GameInstance
  → Print String ("SCORE: " + TotalScore)   Duration = 10.0

Print Stringは確認用です。実際の画面に出すときは、この値をWidgetのTextへ渡します。

確認する

L_Title を開いてPlayし、Spaceを押してください。

  • コインを3枚とも拾ってゴール → 画面が0.5秒かけて暗くなり、リザルトで SCORE: 30
  • 1枚も拾わずゴールSCORE: 0
  • 2枚だけ拾うSCORE: 20
  • タイトルへ戻ってもう一周 → 再び0から始まり、加算されない

うまくいかないときの切り分けです。

  • リザルトが必ず0 → Project SettingsのGame Instance Classが未設定。既定のGameInstanceが使われている
  • コインを拾っても増えないSet TotalScore をGameInstanceではなく、コイン自身やLevel Blueprintの変数に対して行っている
  • 暗転せずに切り替わるDelay の秒数が Duration より短い、または Hold When Finished がオフ
  • ゴールで2回遷移が走るIsTransitioning のBranchが無いか、Trueの側に処理をつないでいる
  • エディタでは動くのにパッケージで真っ暗 → by Nameで指定していて、マップがパッケージに含まれていない
  • ステージで歩けないL_Stage01 のWorld Settingsで GameMode Override が未設定

ここで Set TotalScore の対象をGameInstanceからLevel Blueprintの変数に差し替えて みてください。ステージ中のPrint Stringは正しく増えるのに、リザルトでは何も残っていないのが確認できます。前の節で述べた「ほぼ全部消える」を実際に見ておくと、置き場所の判断に迷わなくなります。

ポイントは2つです。

  • 持ち越したい値は、書く前に置き場所を決める: レベルを跨ぐならGameInstance、そのレベルの中だけならPlayerStateやGameStateで十分です。後から移し替えるのは手間なので、変数を作る前に「これはレベルを跨ぐか」を判断してください
  • 遷移は必ず二重防止をかける: オーバーラップもボタンも、1フレームに複数回反応します。 IsTransitioning のようなフラグを1つ置くだけで、暗転が二重に走ったり遷移が競合したりする不具合を防げます

リザルトの見た目を作り込むところはUMGの記事へ、ハイスコアを次回起動時にも残すところはセーブ/ロードの記事へ続きます。

Sponsored

Level Streamingとの違い

レベルを扱う機能にはもう1つ Level Streaming があります。役割がはっきり違うので、迷ったら次の基準で選んでください。

Open LevelLevel Streaming
やること切り替える繋げる
前のレベル破棄されるそのまま残る
向いている場面タイトル、ステージ選択、リザルト広大なマップ、屋内へのシームレスな移動
プレイヤー作り直される同じまま

「ダンジョンの入口に入ったら中へ移動する」ような演出でも、読み込み中に画面が切り替わってよいならOpen Levelで十分です。逆に、扉を開けたら奥まで地続きに見せたいならLevel Streamingになります。詳しくはLevel Streamingの記事で扱っています。

おまけ:先に知っておくと良いこと

  • 同じレベルをやり直すのもOpen Level: リトライは Get Current Level Name の結果を Open Level (by Name) に渡せば作れます。Actorの状態が完全に初期化されるので、自前でリセット処理を書くより確実です。ただし、死ぬたびにステージ全体をやり直させたくないゲームでは、レベルは開き直さず復帰地点へ戻す作りにします(→ チェックポイントとリスポーン
  • 起動時に開くレベルを変える: 完成が近づいたら Project Settings > Maps & Modes > Game Default MapL_Title にします。ここがテスト用のレベルのままだと、パッケージ版がいきなり開発中の画面から始まります
  • レベルごとにGameModeを変えられる: World Settingsの GameMode Override はレベル単位の設定です。タイトルにはUI操作だけのGameMode、ステージには通常のGameModeを割り当てると、入力モードの切り替えが楽になります
  • Level Blueprintに書きすぎない: Level Blueprintの中身は、そのレベル専用で使い回せません。遷移の処理が育ってきたら、GameModeやGameInstance Subsystemへ移してください(→ Level Blueprintとの使い分け
  • マップは1か所にまとめる: Content/Maps のようなフォルダを決めておくと、パッケージ設定でマップを指定するときも、開き直すときも迷いません(→ アセット管理の記事

まとめ

  • 切り替えは Open Level 1つ。ただし実行はフレームの終わりなので、 後処理は必ず呼ぶ前に
  • 基本は by Object Reference 。綴りを間違えず、パッケージにも確実に含まれる
  • レベルを移ると Actor・Widget・GameMode・PlayerStateはすべて作り直し になる
  • 生き残るのは GameInstance だけ。持ち越したい値はここに置く
  • ロード画面のWidgetは 描画される機会がないので映らない先に暗転を完了させてから Open Levelを呼ぶ
  • 切り替えるなら Open Level、繋げるなら Level Streaming

いま作っているゲームで、ステージを跨いで残ってほしい数値はどれでしょうか。それが変数として置かれている場所を、一度確かめてみてください。