チェックポイントまで進んで、装備を整えて、所持金を貯める。そこでPlayを止めると、すべてが消えます。次に起動したときには、また最初からです。
これを解決するのが Save Game です。ゲームの状態をファイルへ書き出し、次回の起動時に読み戻す仕組みで、Blueprintだけで実装できます。この記事では、Save Gameクラスの作り方から、チェックポイントでセーブしてリスポーンで復帰する実践、そして初心者が必ずはまる「Actorの参照は保存できない」という落とし穴までを解説します。
この記事でわかること
- Save Gameの正体= 持ち出し用の箱 。3手順は 作る・詰める・開ける
- Blueprintでのセーブとロードの 完成ノードグラフ
- 実践: チェックポイントで保存し、死んだら復帰する
- Actor参照を保存してはいけない 理由と、代わりに保存するもの
- ゲーム中のセーブは 非同期 で
Save Gameとは:持ち出し用の箱
ゲーム実行中の値は、すべてメモリの上にあります。プレイヤーのHPも、所持金も、電源を切れば消えます。ファイルとして残すには、 残したい値だけを別の入れ物に移してから書き出す 必要があります。この入れ物が Save Game です。
3つの手順に分かれます。
| 手順 | すること | 使うノード |
|---|---|---|
| 作る | 何を保存するかを決めた「型」を用意する | (Blueprint Classを作成) |
| 詰める | 現在の値を箱にコピーして、スロットへ書き出す | Create Save Game Object / Save Game to Slot |
| 開ける | スロットから箱を取り 出し、値をゲームへ戻す | Load Game from Slot |

スロット はセーブファイルの名前です。"SaveSlot01" のような文字列を指定すると、その名前でファイルが作られます。名前を変えれば、セーブデータを複数持てます。
手順1:箱の型を作る
コンテンツブラウザで右クリックし、 Blueprint Class を選びます。親クラスの選択ダイアログで 「All Classes」の検索欄に SaveGame と入力し、SaveGame を選択してください。よく使うクラスのボタンには並んでいないので、検索から選びます。
名前は BP_SaveGame としておきます。開いたら、保存したい値を変数として追加します。
| 変数名 | 型 | 用途 |
|---|---|---|
SavedHealth | Float | HP |
SavedGold | Integer | 所持金 |
SavedLocation | Vector | プレイヤーの座標 |
SavedItemIDs | Name(配列) | 所持アイテムのID |
このBlueprintには ロジックを書きません 。値を並べておくだけの入れ物として使います。
補足: 保存する変数は最小限にしてください。ロード時に計算し直せる値(最大HP、アイテムの表示名、装備による攻撃力の合計など)は保存しなくて構いません。保存する項目が増えるほど、後でセーブデータの互換性を保つのが大変になります。
手順2:詰めて保存する
セーブ処理は「箱を用意する」「値を移す」「書き出す」の順です。

BP_PlayerCharacter(またはGameInstance)
Custom Event: SaveGame
→ Create Save Game Object(Save Game Class: BP_SaveGame)
Return Value → Promote to variable(名前: SaveRef)
→ Set Saved Health(Target: SaveRef, Value: CurrentHealth)
→ Set Saved Gold(Target: SaveRef, Value: Gold)
→ Set Saved Location(Target: SaveRef, Value: GetActorLocation)
→ Save Game to Slot(Save Game Object: SaveRef, Slot Name: "SaveSlot01", User Index: 0)
Create Save Game Object の Save Game Class ピンで、作った BP_SaveGame を選ぶのを忘れないでください。ここが空だと箱ができません。
Save Game to Slot は成功したかどうかを bool で返します。この戻り値を Branch に繋いでおくと、書き込みに失敗したときに気づけます。
注意:
Save Game to Slotは書き込みが終わるまで処理を止めます。数値だけの小さなセーブなら一瞬ですが、ゲームプレイ中に呼ぶと一瞬引っかかることがあります。対処はおまけで扱います。
手順3:開けて戻す
ロードは逆順です。ただし、 2つの手続きが要ります 。

Custom Event: LoadGame
→ Does Save Game Exist(Slot Name: "SaveSlot01", User Index: 0)
→ Branch
True →
Load Game from Slot(Slot Name: "SaveSlot01", User Index: 0)
Return Value → Cast To BP_SaveGame
As BP Save Game →
Set Current Health(Value: Saved Health)
→ Set Gold(Value: Saved Gold)
→ Set Actor Location(New Location: Saved Location)
False →
Print String("セーブデータがありません")
1つ目が Does Save Game Exist による存在確認 です。初回起動時にはセーブフ ァイルがないので、これがないと「なぜか何も起きない」状態になります。
厳密にはこの確認は必須ではありません。Load Game from Slot は失敗すると None を返すので、Is Valid で受けても同じことができます。ただし、 ファイルはあるのに壊れている・型が違う というケースもあるため、次のCastの失敗側も塞いでおくのが確実です。
2つ目が Cast To BP_SaveGame です。Load Game from Slot が返すのは基底の Save Game 型で、そのままでは SavedHealth などの変数が見えません。Castを挟んで自分のクラスに変換すると、変数が読めるようになります(→ Castの記事)。
「セーブはできているのにロードで値が取れない」の原因は、ほぼこのCast忘れです。
実践:チェックポイントで保存し、復帰する
ここまでの部品で、実際に使える仕組みを組みます。アクションゲームのチェックポイント、ローグライクの階層セーブ、オープンワールドの拠点セーブ。「ある地点を通過したら記録し、死んだらそこへ戻す」構図は、どのジャンルでも同じです。
完成形
チェックポイントに触れると保存され、その後で落下死しても、 触れた時点のHP・所持金・位置か ら再開 します。

再現条件
BP_SaveGame に、手順1の変数のうち3つを用意します。
| 変数 | 型 | 初期値 |
|---|---|---|
SavedHealth | Float | 100.0 |
SavedGold | Integer | 0 |
SavedLocation | Vector | (0, 0, 0) |
BP_ThirdPersonCharacter には次を用意します。
| 変数 | 型 | 初期値 |
|---|---|---|
CurrentHealth | Float | 100.0 |
Gold | Integer | 0 |
IsDead | Boolean | false |
さらに BP_Checkpoint(親は Actor)を作り、Box Collisionコンポーネントを追加します。詳細パネルで Generate Overlap Events がオン 、Collision Presetが OverlapAllDynamic(Pawnに反応する設定)になっていることを確認してください。ここが既定のままだと、触れてもイベントが飛びません。
加えて、動かすための下ご しらえが3つ要ります。
| 用意するもの | 内容 |
|---|---|
| GameModeの設定 | Default Pawn Class が BP_ThirdPersonCharacter になっていること |
| レベルへの配置 | BP_Checkpoint を1つ配置する |
| 所持金を増やす手段 | 動作確認用に、キー入力(1 キーなど)で Gold に +100 するイベントを仮に付けておく |
チェックポイント側
BP_Checkpoint のBox Collisionで On Component Begin Overlap を取り、触れたのがプレイヤーなら、プレイヤー側のセーブ処理を呼びます。
BP_Checkpoint
On Component Begin Overlap(Box)
→ Cast To BP_ThirdPersonCharacter(Object: Other Actor)
As BP Third Person Character →
Save Game(プレイヤーのCustom Eventを呼ぶ)
→ Print String("チェックポイント記録")
プレイヤー側
手順2・手順3で作ったセーブ/ロードのイベントをそのまま使います。位置の保存は GetActorLocation を、復帰時は SetActorLocation を使います。
次に「死んだとき」を作ります。ここが最初のつまずきどころで、 UEの標準のKill Zに任せてはいけません 。Kill Zに落ちたCharacterは破棄されるので、そのCharacterに書いた復帰処理は動かないためです。
代わりに、落下を自前で判定します。BP_ThirdPersonCharacter のTickか、床下に置いたBox Collision(OnActorBeginOverlap)から OnDeath を呼びます 。
BP_ThirdPersonCharacter
Event Tick
→ Get Actor Location → Break Vector(Z)
→ Z < -500 かつ IsDead == false
→ Branch True → Set IsDead(true)→ OnDeath
復帰処理では、ロードの前に 落下速度をリセット してください。SetActorLocation で座標だけ戻しても、落下中の速度が残っていると、戻った直後にまた落ちていきます。
Custom Event: OnDeath
→ Stop Movement Immediately(Target: Character Movement)
→ Load Game(手順3のCustom Event)
→ Set IsDead(false)
Stop Movement Immediately のTargetには Character Movement コンポーネントを繋ぎます。
補足: この作りは、Characterを作り直す「リスポーン」ではなく、 同じCharacterを保存地点へ戻して状態を復元する ものです。個人開発ではこちらのほうが扱いやすく、UIやカメラの参照も切れません。
確認する
1 キーで所持金を300まで増やし、チェックポイントに触れてから、床の端から落ちてください。 位置がチェックポイントに戻り、所持金が300のままであれば成功 です。
- 所持金が0に戻る → セーブ時の
Set Saved Goldの接続漏れ、またはロード時のCast忘れ - 位置は戻るが落下し続ける →
Stop Movement Immediatelyが抜けている - 落ちても何も起きない →
OnDeathが呼ばれていない。Kill Zに任せていないか、Z座標のしきい値を確認 - チェックポイントに触れても反応しない → Box Collisionの
Generate Overlap EventsかCollision Presetの設定
もう1つ、 PIEを停止してから再度Playし、ロードだけを実行 してみてください。ここで前回の値が戻れば、メモリ上ではなく ファイルに残っている ことの確認になります。同一Play中のテストだけでは、ここまでは確かめられません。
ポイントは2つです。
- セーブは「今の値を箱へ写す」だけ: チェックポイントは合図を送る役に徹し、何を保存するかはプレイヤー側が決めます。この分担にしておくと、保存項目が増えてもチェックポイント側を触らずに済みます
- 復帰は座標だけでは足りない: 速度、入力の状態、再生中のアニメーション。位置を戻したあとに残っている「動きの続き」を止める処理が要ります。ここを忘れると「戻ったのにまた死ぬ」という挙動になります
セーブする項目が10個を超えてきたら、Structでまとめると、ノードの数を増やさずに項目を増やせます。
保存してはいけないもの
Save Gameの変数には、Actor参照やコンポーネント参照も 入れられてしまいます 。エラーも出ません。しかし、これを当てにした設計は壊れます。

参照が指しているのは「そのオブジェクトそのもの」であって、中身のデータではありません。レベルに最初から配置してあるActorのように条件が揃えば復元されることもありますが、 実行中に生成したActorは復元されず 、参照先が持っていたHPなどの状態も一緒には保存されません。「動くこともあるが、当てにできない」というのが実態です。
| 保存しない | 代わりに保存する |
|---|---|
| 装備中の武器Actorへの参照 | 武器のID(Name や Integer) |
| インベントリ内のアイテムObject | アイテムIDの配列 |
| 現在いる部屋のActor参照 | 部屋の名前、またはプレイヤーの座標 |
| Widgetへの参照 | 開いていた画面を表すEnum |
そのため 値だけを保存し、ロード時にそこから作り直す のが基本方針になります。武器IDから実物を復元するには、IDと実体を対応づける表が要るので、Data TableやData Assetと組み合わせます。
おまけ:先に知っておくと良いこと
アップデートで保存する項目が増える日に備える。 SaveGameクラスに変数を足すこと自体は安全で すが、 名前や型を変えると古いセーブの値が失われます 。SaveVersion を1つ持っておくだけで対処できるので、セーブを作る今のうちに入れておくのが得です(→ セーブデータのバージョン管理)。
- ゲーム中のセーブは非同期で:
Save Game to Slotの代わりにAsync Save Game to Slotを使うと、ディスクへの書き込みが裏で進むため、引っかかりを軽くできます。ただし 完全に止まらなくなるわけではありません 。値を並べ直す処理はゲームスレッドで走るので、保存項目が多いと影響は残ります。完了時にはCompletedピンが呼ばれますが、 これは成功・失敗のどちらでも呼ばれます 。SuccessをBranchに通し、Trueのときだけ「セーブしました」を出してください - セーブデータの置き場所: エディタ実行時は
プロジェクトフォルダ/Saved/SaveGames/SaveSlot01.sav、パッケージ後のWindowsでは%LOCALAPPDATA%/プロジェクト名/Saved/SaveGames/です(保存先はプラットフォームごとに違います)。動作確認のときは、このファイルを削除すれば「初回起動」の状態を再現できます - どこからセーブを呼ぶか: プレイヤーに書いた処理そのものは消えませんが、 レベルを移動するとCharacterが作り直されるため、変数に入れていた値は失われます 。ファイルに書き出すほどではない「レベル間だけの持ち越し」なら、Game Instanceに置くだけでゲーム中は消えません(仕組 みの解説はSubsystem/GameInstanceにもあります)
- 後から項目を増やすとき:
BP_SaveGameに変数を追加しても、古いセーブデータは読み込めます(増えた変数は初期値になります)。ただし変数名を 変更・削除 すると、その値は失われます。名前は最初に決め切るつもりで付けてください - セーブデータは書き換えられる:
.savはプレイヤーが触れる場所にある普通のファイルです。オフラインの個人開発ゲームであれば、まずは気にしなくて構いません
// C++で型を定義する場合
UCLASS()
class MYPROJECT_API UMySaveGame : public USaveGame
{
GENERATED_BODY()
public:
UPROPERTY()
float SavedHealth = 100.0f;
UPROPERTY()
int32 SavedGold = 0;
UPROPERTY()
FVector SavedLocation = FVector::ZeroVector;
};
保存・読み込みは UGameplayStatics::SaveGameToSlot / LoadGameFromSlot、非同期版は AsyncSaveGameToSlot / AsyncLoadGameFromSlot を使います。 UPROPERTY() を付け忘れた変数は保存されません 。ここがC++で最も多いつまずきです。
なお UPROPERTY() だけで保存されます(SaveGame 指定子は不要です)。この変数をBlueprintからも読み書きするなら、BlueprintReadWrite を追加してください。
まとめ
- Save Gameは 持ち出し用の箱 。3手順は 作る(クラス)・詰める(Save to Slot)・開ける(Load from Slot)
- ロードには
Does Save Game Existによる存在確認 とCast Toによる型変換 が要る。値が取れない原因はほぼCast忘れ - Actorやオブジェクトへの参照は当てにしない 。IDや座標という値で保存し、ロード時に再構築する
- 復帰処理では 位置だけでなく速度も戻す 。忘れると戻った直後にまた落ちる
- ゲームプレイ中のセーブは
Async Save Game to Slotで引っかかりを減らす。Completedは失敗時も呼ばれるのでSuccessで分岐する
あなたのゲームで、プレイヤーが「ここまでの進みが消えたら嫌だ」と思うのはどの瞬間でしょうか。そこが最初のチェックポイントの置き場所です。