
C言語入門|STEP19:セーブ・ロード処理の実装-ゲーム状態構造体とファイル操作
はじめに:途中でやめられるRPGは親切
STEP18では、マップ探索とイベント分岐の仕組みを解説しました。
STEP19では、RPGを「最後まで安心して遊べるもの」にするための
セーブ・ロード機能とイベント処理 を扱います。
このSTEPのポイントは、
- ゲームの状態をまとめて保存する。
- 再開時に正しく復元する。
- イベント進行を一度きりに管理する。
という点です。
RPGでは、この仕組みがあるかどうかで完成度が大きく変わります。
セーブ対象となるゲーム状態とは
まず、セーブ対象を整理してみましょう。
このRPGでは、次の情報をまとめて保存します。
| 要素 | 内容 |
|---|---|
| パーティ | キャラのHP・MP・レベルなど |
| 所持アイテム | インベントリ配列 |
| プレイヤー位置 | マップ上の座標 |
| マップ状態 | 敵や宝箱の消費状況 |
| 進行フラグ | ボス討伐など |
これらをまとめたものが、
GameState 構造体 です。
GameState 構造体の役割
ゲーム全体の状態は、
次のような構造体で管理されています。
typedef struct {
Character party[PARTY_SIZE];
int inv[ITEM_COUNT];
int px, py;
char map[MAP_H][MAP_W];
int game_clear;
} GameState;この設計のポイント
- ゲームに必要な情報を1つに集約
- セーブ・ロード処理が簡単
- 関数引数として扱いやすい。
ゲーム全体=1つの構造体
という考え方は、とても重要です。
セーブ処理の基本構造
セーブ処理では、
GameState をそのままファイルに書き込みます。
static void save_game(const GameState* gs) {
FILE* fp = fopen("save.dat", "wb");
if (!fp) return;
fwrite(gs, sizeof(GameState), 1, fp);
fclose(fp);
}何をしている処理か
- バイナリ書き込みモードでファイルを開く。
- GameState を一括で書き込む。
- ファイルを閉じる。
構造体を丸ごと保存するため、
処理がとてもシンプルです。
バイナリ保存を使う理由
テキストではなく、
バイナリ形式で保存している理由は次のとおりです。
| 理由 | 内容 |
|---|---|
| 実装が簡単 | fwrite 1回で完了 |
| 読み書きが高速 | データ変換不要 |
| 学習向き | 仕組みが分かりやすい。 |
学習用RPGとしては、
最も分かりやすいセーブ方式 です。
ロード処理の基本構造
ロード処理は、
セーブの逆を行います。
static int load_game(GameState* gs) {
FILE* fp = fopen("save.dat", "rb");
if (!fp) return 0;
fread(gs, sizeof(GameState), 1, fp);
fclose(fp);
return 1;
}ロード処理の流れ
- ファイルが存在するか確認
- GameState を一括で読み込み。
- 読み込み成功を返す。
失敗時には、
新規ゲームに進む設計です。
セーブ・ロードをメニュー化する理由
セーブとロードは、
マップ探索中に選択できます。
printf("s:セーブ l:ロード 移動キー\n");この設計のメリット
- いつでも中断できる。
- 状況に応じて使い分けできる。
- テストプレイがしやすい。
操作性の良さ=遊びやすさ
につながっています。
マップ状態も保存している理由
GameState には、
マップ配列も含まれています。
char map[MAP_H][MAP_W];これが重要な理由
- 倒した敵が復活しない。
- 開けた宝箱が再出現しない。
- 探索済み状態が保持される。
単に位置だけを保存するのではなく、
世界の状態そのものを保存 しているのがポイントです。
イベントを一度きりにする仕組み
イベント処理では、
実行後にマップを書き換えます。
g_map[y][x] = '.';この方法のメリット
- フラグ管理が不要
- マップが進行状況を記憶する。
- 実装が非常にシンプル
イベントを「消す」ことで、
再発を防いでいます。
宿・回復イベントの処理例
回復イベントでは、
パーティ全体を回復します。
for (int i = 0; i < PARTY_SIZE; i++) {
party[i].hp = party[i].max_hp;
party[i].mp = party[i].max_mp;
}なぜ全回復なのか
- プレイヤーに安心感を与える。
- 難易度を緩和する役割
- 探索継続を後押しする。
イベントは、
ゲームバランス調整の重要な要素 です。
ゲームクリアフラグの役割
ボスを倒すと、
次のフラグが立ちます。
gs->game_clear = 1;フラグ管理の意味
- エンディング表示の分岐
- 再戦を防ぐ
- ゲーム終了条件を明確化
状態をフラグで管理することで、
処理がとても分かりやすくなります。
セーブデータが壊れた場合への配慮
ロード失敗時には、
次のように分岐します。
if (!load_game(&gs)) {
printf("セーブデータがありません\n");
}この処理がある理由
- ファイル未存在に対応
- ゲームが止まらない。
- 安心して操作できる。
失敗を前提にした設計 は、
実用的なプログラムの基本です。
STEP19で押さえておきたいポイントまとめ
STEP19の重要ポイントはこちらです。
- ゲーム状態は1つの構造体で管理する。
- セーブは構造体を丸ごと保存する。
- マップ状態も保存対象に含める。
- イベントは一度きりで処理する。
- フラグで進行状態を明確にする。
