ステージ1でコインを30枚集めて、扉に入る。次のステージが始まった瞬間、画面のカウントが0に戻っています。変数はちゃんとPlayerに持たせたはずなのに、値だけがどこかへ消えています。
消えたのではなく、その Player自体が作り直されている のがこの現象の正体です。UE5でレベルをまたいで生き残る入 れ物は1つだけで、それが Game Instance です。この記事では、Game Instance Blueprintの作り方とProject Settingsへの登録、Get Game Instance からの読み書き、Init と Shutdown の使いどころ、そして置いていいものと悪いものを解説します。
この記事でわかること
- レベルを移ると 何が作り直され、何が生き残るのか
- Game Instance Blueprintの作り方と、 忘れると何も起きないProject Settingsの登録
- 読み書きの定番
Get Game Instance→Cast ToInit/Shutdownはいつ呼ばれるのか- Game Instanceに 置いていいもの・置いてはいけないもの
- Save Game と Subsystem との役割の違い
- 実践: 3ステージ通しのスコアとクリア記録を持ち越す
生き残るのはGame Instanceだけ
まず、それぞれの入れ物が いつからいつまで生きているのか を時間軸で見ます。

| 入れ物 | 生きている期間 | レベルを移ると |
|---|---|---|
| Game Instance | アプリの起動から終了まで | そのまま残る |
| GameMode / GameState | レベルの開始から終了まで | 作り直される |
| PlayerState | 同上 | 作り直される |
| Character・配置したActor | 同上 | 破棄される |
| Widget | 追加してから破棄まで | 破棄される |
| Level Blueprintの変数 | そのレベルの中だけ | 消える |
Game Instanceだけが別格です。 最初のレベルが読み込まれるより前に作られ、アプリを閉じるまで1つのまま存在し続けます。 レベルの外側にいるので、レベルが入れ替わっても影響を受けません。
逆に言うと、Game Instance以外のどこに変数を置いても、レベルをまたいだ時点で初期値へ戻ります。「PlayerStateに入れておけば大丈夫だと思った」という詰まり方が多いのですが、PlayerStateもレベル単位の入れ物です(→ レベル遷移の記事)。
判断はこの一言で決まります。 Playを止めるまで覚えていてほしい値は、Game Instanceに置く。
作って、登録する
手順は3つで、 3つ目を忘れると何も起きません 。

1. Blueprint Classを作る
コンテンツブラウザで右クリック → Blueprint Class 。親クラスの選択ダイアログで ALL CLASSES を開き、検索欄に GameInstance と入力して選びます。よく使うクラスのボタンには並んでいないので、検索か ら選んでください。名前は BP_GameInstance にします。
2. 持ち越したい変数を追加する
開いて、My Blueprintパネルから変数を足します。初期値は Class Defaults で設定します。Actorと違い、レベルに置かれるものではないので、詳細パネルの Instance Editable は使いません。
3. Project Settingsで登録する
Edit > Project Settings > Project > Maps & Modes を開き、 Game Instance カテゴリの Game Instance Class を BP_GameInstance に設定します。
この3つ目が本記事で最も事故の多い箇所です。 登録していないと、エンジンは既定の GameInstance を使い続けます。 自作クラスは作られないので、次の節で書くCastが毎回失敗し、変数の読み書きが丸ごと素通りします。エラーも警告も出ません。
「Game Instanceを作ったのに値が保持されない」と感じたら、コードを疑う前にここを開いてください。
補足: 設定はプロジェクト単位です。変更したらPlayを一度止めて、開き直してから確認してください。実行中のPIEには反映されません。
読み書きはGet Game InstanceからCast
どのBlueprintからでも、2ノードで届きます。Actorでも、Widgetでも、Level Blueprintでも同じです。

Get Game Instance
→ Cast To BP_GameInstance
As BP Game Instance →
Set Total Score(Value: Total Score + 10)
Get Game Instance が返すのは基底の Game Instance 型なので、そのままでは自分で足した変数が見えません。 Cast To BP_GameInstance を挟むと、変数と関数が並びます。
Castの失敗ピンをつないでおくと、登録忘れにその場で気づけます。
Get Game Instance
→ Cast To BP_GameInstance
Cast Failed → Print String("Game Instance Class が未設定です")
Castが重いのではないかと気になるかもしれませんが、ここでは心配は要りません。 BP_GameInstance はProject Settingsで指定されている時点で必ず読み込まれている ため、Castによって余計なアセットが引きずられることがないからです。Castが問題になるのはどういう場合かはCast と Blueprint Interfaceの記事にまとめています。
それでも Get Game Instance → Cast の2ノードを毎回書くのが煩わしくなったら、次の2つの手があります。
- Game Instance側に関数を作る:
AddScore(Amount)のような関数をGame Instance Blueprintに用意すると、呼ぶ側は「Cast → 関数を1つ呼ぶ」で済みます。加算のルールが1箇所にまとま るので、後から「2倍イベント中は倍率をかける」といった変更も1箇所で終わります - Subsystemへ移す: Subsystemには専用のGetノードが用意されるため、Castが不要になります(後述)
InitとShutdown
Game Instance Blueprintのイベントグラフで右クリックすると、 Event Init と Event Shutdown を追加できます。

| イベント | 呼ばれるタイミング | 回数 |
|---|---|---|
| Init | ゲームの起動時。 最初のレベルが読み込まれる前 | 1回だけ |
| Shutdown | ゲームの終了時 | 1回だけ |
レベルを移動しても呼ばれません。 ここがActorの Event BeginPlay との決定的な違いです。BeginPlayはレベルが変わるたびに走りますが、Initは起動時の1回きりです。
エディタで確認するときは、 Playを押した時にInit、Playを止めた時にShutdownが走る と考えてください。PIEのセッションごとにGame Instanceが作り直されるためです。
Initに書くと良いのは、次のような「起動時に1回だけやること」です。
- セーブデータの存在確認と、あればロード
- 音量やキーコンフィグなど、設定値の読み込み
- 乱数シードの決定
ただし1つ注意があります。 Initの時点では、レベルもプレイヤーもまだ存在しません。 Get Player Character は None を返します。プレイヤーに触る処理は、Initではなくレベル側のBeginPlayに置いてください。
置いていいもの・悪いもの
Game Instanceは便利なので、置き場所に困ったものが全部集まってきます。ここで1つだけ、はっきりした線があります。 参照は置かない。値を置く。

| 置いていい(値) | 置いてはいけない(参照) |
|---|---|
| スコア、所持金、残機(Integer / Float) | プレイヤーCharacterへの参照 |
| クリア済みステージのID(Name の配列) | レベルに置いたActorへの参照 |
| 所持アイテムのIDと個数(Map) | 表示中のWidgetへの参照 |
| 難易度や選択キャラのEnum | Componentへの参照 |
| 音量・キーコンフィグの設定値 | そのレベルにしか無い座標や部屋番号 |
理由は単純で、 参照先が次のレベルには存在しないから です。レベル遷移で破棄されたActorを指していた変数は None になり、そこから関数を呼ぶと Accessed None のエラーになります。
やっかいなのは、 同じレベル内でテストしている間は動いてしまう ことです。遷移して初めて壊れるので、原因に気づくのが遅れます。持ち越したいのが「あの敵」なら、Actor参照ではなく その敵を再現できるID を置いてください。
もう1つ、置きすぎにも線を引いておきます。Game Instanceは1つしかないので、変数を足せば足すほど、無関係な機能が同じクラスに同居します。 「レベルをまたぐか」で判断して、またがないものは入れない のが、後で読める状態を保つコツです。
Save Gameとの違い
よく混同されますが、担当している範囲がまったく違います。
| Game Instance | Save Game | |
|---|---|---|
| どこにある | メモリ | ディスク上のファイル(.sav) |
| 生きている期間 | アプリを閉じるまで | 削除するまで |
| レベル遷移 | 残る | 残る |
| アプリの再起動 | 消える | 残る |
| 読み書きの手間 | 変数を直接触るだけ | 箱に詰めて書き出し、読んでCast |
Game Instanceは「今回のプレイ中の記憶」、Save Gameは「次回に渡す記録」 です。競合するものではなく、組み合わせて使います。
よくある形は次のとおりです。ゲーム中の加算はGame Instance上で行い、区切りの良いところでSave Gameへ書き出す。起動時のInitで読み戻してGame Instanceへ入れる。こうすると、ゲーム中の処理は毎回ファイルを触らずに済みます。
Subsystemとの使い分け
GameInstance Subsystem は、Game Instanceと まったく同じ寿命 を持つ入れ物です。起動時に作られ、レベル遷移で壊れず、終了時に消えます。
違うのは、 置き方の粒度 だけです。
| Game Instance | GameInstance Subsystem | |
|---|---|---|
| 何であるか | 持ち越すための1つの置き場所 | 機能ごとに分ける仕組み |
| 数 | プロジェクトに1つ | 機能の数だけ作れる |
| 作成 | Blueprintだけで作れる | C++が必要 |
| 取得 | Get Game Instance → Cast | 専用のGetノード(Castなし) |
| 生存期間 | 起動から終了まで | 同じ |
判断はこの順です。
- まずGame Instanceで始める 。Blueprintだけで完結し、変数を1つ足せば動きます
- スコア・音量・セーブ管理・実績のように 無関係な機能が同居し始めたら 、機能ごとにSubsystemへ切り出す
つまり、この記事で扱っているのが 置き場所 、Subsystemの記事で扱っているのが その置き場所を機能ごとに分ける仕組み です。先にGame Instanceで動く形を作ってから、必要になった段階でSubsystemへ移す順番で構いません。移すときも、Game Instance側に関数としてまとめてあれば、中身をそのまま持っていけます。
実践:3ステージ通しのスコアを持ち越す
ステージクリア型アクションの累計スコア、ローグライクの1周の稼ぎ、パズルゲームの連戦記録。 「複数のレベルを通した合計」 は、ジャンルを問わず必要になります。ここでは3つのステージを通してスコアを足し込み、どのステージをクリアしたかも記録します。
動かすとこうなる
3つのステージのゴールに順に触れると、スコアが 120 → 200 → 350 と積み上がり、リザルトに合計とクリア数が出ます。

再現条件
Third Person テンプレート で新規プロジェクトを作り、次を用意します。
レベル4つ(File > New Level > Basic で作成し、Content/Maps へ保存。それぞれ Player Start を置き、World Settingsの GameMode Override を BP_ThirdPersonGameMode にします)
| レベル名 | 置くもの |
|---|---|
L_Stage01 | BP_StageGoal を1つ |
L_Stage02 | BP_StageGoal を1つ |
L_Stage03 | BP_StageGoal を1つ |
L_Result | なし |
BP_GameInstance(親クラス: GameInstance)の変数
| 変数名 | 型 | 初期値 |
|---|---|---|
TotalScore | Integer | 0 |
ClearedStages | Name( Array ) | (空) |
作成後、 Edit > Project Settings > Project > Maps & Modes > Game Instance Class を BP_GameInstance に設定します。
BP_StageGoal(親クラス: Actor)
| 要素 | 設定 |
|---|---|
| Box Collision | Box Extent 100 / 100 / 100 、Collision Preset OverlapAllDynamic |
変数 StageID(Name / Instance Editable ) | 既定値 Stage01 |
変数 StageScore(Integer / Instance Editable ) | 既定値 0 |
変数 NextLevel(Level(Object Reference)/ Instance Editable ) | 未設定 |
変数 IsTransitioning(Boolean) | false |
Instance Editable にするのが要点です。1つの BP_StageGoal を3つのレベルに置いて、 配置した個体ごとに 詳細パネルから値を変えます。
| 配置先 | StageID | StageScore | NextLevel |
|---|---|---|---|
L_Stage01 | Stage01 | 120 | L_Stage02 |
L_Stage02 | Stage02 | 80 | L_Stage03 |
L_Stage03 | Stage03 | 150 | L_Result |
Game Instance側:ClearStage関数
BP_GameInstance の Functions に ClearStage を新規作成し、入力ピンを2つ 足します。
| 入力ピン | 型 |
|---|---|
StageID | Name |
Score | Integer |

関数: ClearStage(入力: StageID / Name, Score / Integer)
→ Add Unique(Target: ClearedStages, New Item: StageID)
→ Set Total Score(Value: Add(A: Total Score, B: Score))
→ Print String(Append: "TOTAL: " + Total Score を文字列化)
Add ではなく Add Unique を使っています。同じステージをやり直したときに、クリア記録が二重に積まれないようにするためです。 Add Unique は、すでに同じ値が入っていれば何もせずインデックスだけを返します。
スコアの加算はここに集約しておきます。 TotalScore を書き換える場所を1つに固定しておくと 、後で「どこかで勝手に0にされている」を探し回らずに済みます。
ゴール側
BP_StageGoal のBox Collisionで On Component Begin Overlap を取ります。
BP_StageGoal
On Component Begin Overlap(Box)
→ Cast To BP_ThirdPersonCharacter(Object: Other Actor)
→ Branch(Condition: IsTransitioning)
True → (何もし ない)
False ↓
→ Set IsTransitioning(true)
→ Get Game Instance
→ Cast To BP_GameInstance
As BP Game Instance →
ClearStage(StageID: StageID, Score: StageScore)
→ Open Level (by Object Reference)(Level: NextLevel)
ClearStage を Open Level より前に呼ぶ ところが重要です。Open Level の後ろにつないだノードは、今のレベルが消える直前に走ることになり、順序に依存した処理が壊れます(→ レベル遷移の記事)。
IsTransitioning のBranchは二重反応の防止です。オーバーラップは1フレームに複数回入ることがあり、これが無いと加算が2回走ります。
リザルト側
L_Result のLevel Blueprintで読み出します。
Event BeginPlay
→ Get Game Instance
→ Cast To BP_GameInstance
As BP Game Instance →
Print String(Append: "TOTAL: " + Total Score, Duration: 10.0)
→ Length(Target: Cleared Stages)
→ Print String(Append: "CLEARED: " + 結果, Duration: 10.0)
確認する
L_Stage01 を開いてPlayし、ゴールに触れて3ステージを通してください。
- ゴールに触れるたび、画面に
TOTAL: 120→TOTAL: 200→TOTAL: 350と出る L_ResultでTOTAL: 350とCLEARED: 3が10秒間表示される- Output Logで
Initのログを出しておくと、 Playの開始時に1回だけ 出る。レベルを移っても出ない
うまくいかないときの切り分けです。
- リザルトが常に0、途中のPrint Stringも出ない → Project SettingsのGame Instance Classが未設定。既定のGameInstanceが使われていて、Castが失敗している
- 各ステージの表示は正しいのに、リザルトだけ0 → リザルト側でGame Instanceではなく、そのレベルのLevel Blueprint変数を読んでいる
TOTAL: 240のように加算が倍になる →IsTransitioningのBranchが無く、オーバーラップが2回走っているCLEAREDが3より多い →Add UniqueではなくAddを使っている。同じステージを再クリアした分が積まれる- ゴールに触れても反応しない → Box Collisionの Collision Preset が
OverlapAllDynamicになっていない - 次のレベルへ行かない → 配置した個体の
NextLevelが未設定(Instance Editable がオフだと、そもそも設定欄が出ない)
ここで一度、 Project Settings の Game Instance Class を空に戻してPlayしてみてください。 ゴールに触れても何も表示されず、加算も一切起きません。Castが静かに失敗するだけで、エラーは出ません。この挙動を一度見ておくと、「作ったのに効かない」に出くわしたとき、真っ先に正しい場所を開けるようになります。
ポイントは2つです。
- 書き換える場所を関数1つに閉じる:
TotalScoreを外から直接Setせず、ClearStageを通してのみ変更しています。どこからでも触れる置き場所ほど、入口を狭くしておく価値があります。倍率イベントや実績判定を足すときも、この関数の中だけで済みます - 持ち越すのは値であって、参照ではない: 記録しているのは
NameとIntegerだけです。「クリアしたゴールActor」を配列に入れたくなりますが、次のレベルでは全部Noneになります。IDで持って、必要ならData Assetから実体を引き直してください
この合計を次回の起動にも残すならセーブ/ロードの記事へ、リザルト画面をPrint Stringから本物のUIにするならUMGの記事へ続きます。
おまけ:先に知っておくと良いこと
- Game Instanceには見た目が無い: Actorではないので、レベルに配置することも、位置を持つこともできません。Componentも追加できない前提で設計してください。「持ち回りたい機能」がComponentの形をしているなら、それはComponentとしてプレイヤー側に挿し、値だけをGame Instanceへ預けるほうが素直です
- Tickは使わない方向で考える: Game Instanceで毎フレームの処理を回すと、レベルの状態と関係なく走り続けます。時間の計測が必要なら、値の更新はイベント側から呼ぶ形にしてください(→ Tickを減らす設計)
- リトライ時の初期化を忘れない: 2周目のために
TotalScoreを0へ戻す処理が要ります。置き場所はタイトル画面側です。リザルト側で戻すと、リザルトが自分の表示する値を消してしまいます - パッケージ版とエディタで挙動が違って見えることがある: エディタではPlayを止めるたびにGame Instanceが作り直されますが、パッケージ版ではアプリを閉じるまで残り続けます。「エディタでは毎回リセットされるのに、製品版では前回の値が残っている」という差はここから来ます(→ パッケージングの記事)
- デバッグは値を出して確認する: Game Instanceの変数はレベル上のどのActorにも属さないため、World Outlinerから選んで詳細パネルで覗くという確認ができません。要所に
Print Stringを置くか、Output Logへ出す習慣をつけると追いやすくなります(→ Print Stringとログの記事)
まとめ
- レベルを移ると Actor・Widget・GameMode・PlayerStateは作り直される 。生き残るのは Game Instance だけ
- 作る手順は3つ。3つ目の Project Settings > Maps & Modes > Game Instance Class への登録 を忘れると、何も起きずエラーも出ない
- 読み書きは
Get Game Instance→Cast To。Castの失敗ピンにPrint Stringを挿しておくと登録忘れに気づける Initは起動時に1回だけ 。レベル遷移では呼ばれず、その時点ではプレイヤーもレベルも存在しない- 置くのは 値 。Actor参 照やWidget参照は、次のレベルで
Noneになる - Game Instanceは今回のプレイ中の記憶、Save Gameは次回へ渡す記録 。機能ごとに分けたくなったらSubsystemへ
いま作っているゲームで、タイトルに戻るまで覚えていてほしい数値はいくつありますか。その一覧が、そのまま BP_GameInstance の変数リストになります。