Webゲームの進行を保存する(何を保存しないか・壊れても落ちない形)
ブラウザで遊ぶパズルに「どこまでクリアしたか」と自己ベストを保存しました。盤面は保存しません。保存先がブラウザの領域であること、壊れた保存データで落ちない書き方、記事を書いていて気づいた「版が無い」という穴と、その直し方を残します。
公開 2026年9月19日
この記事の要点4 points
- 保存するのは面の番号と自己ベストだけ。 盤面は保存しない(面は番号から毎回同じものが作られる)。
- だから面の作り方を変えると、同じ番号が別の面になる。 保存データの先頭に版を書いて、知らない版は全部捨てる形にした。
- 保存データは簡単に壊れる。 読めない部分は捨てて、読めた分だけ使う。落ちるより忘れるほうがまし。
- Web の
PlayerPrefsはそのブラウザの中にしか残らない。 端末をまたぐ記録が欲しいなら、保存先はサーバーになる。
目次6章
検証した環境最終確認 2026年9月19日
- Windows 11
- Unity 6.6(6000.6.0f1)・WebGL ビルド
- Chrome(Android の見た目で確認)
- Claude Code(Opus 5 / 思考レベル 高)
書いてあるのは、この環境で実際に試した結果です。ツールの仕様や料金は変わるので、重要な判断の前には公式の情報も確認してください。
ブラウザで遊ぶパズルに、進行の保存を入れました。保存するものはこれだけです。
2種類
保存している値
面の番号と自己ベスト
0
保存している盤面
番号から毎回作れる
1行
保存データの中身
v=1;1=8;2=11
36件
通しているテスト
壊れた保存データ込み
盤面を保存しない
このゲームの面は、番号から自動で作られます。面1なら必ず同じ盤、面2なら必ず同じ盤。作るときに使う種は 面の番号 × 7919 と決めているので、誰の端末でも同じ面が出ます。
つまり盤面はいつでも作り直せるので、保存する意味がありません。保存するのは、作り直せないものだけです。
保存するのは、作り直せないものだけ。
保存データはこの形にしました。
v=1;1=8;2=11;3=17
「版が1・面1は8手・面2は11手・面3は17手」という意味です。JSON にしなかったのは、壊れたときに部分的に読めるほうがいいからです。この形なら、真ん中が欠けても前後は読めます。
記事を書いていて気づいた穴:版が無かった
ここまで書いたところで、自分の手落ちに気づきました。最初の版には v=1 が無かったのです。
面は番号から作られます。ということは、面の作り方を変えたら、同じ番号が別の面になる。そのとき古い自己ベストが残っていると、「8手でクリア」と表示されているのに、いまの面はそもそも8手で解けない、ということが起きます。
最初問題
`1=8;2=11` だけを保存していた。面の中身が変わることを考えていない
気づいた要注意
記事に「面は番号から作られる」と書いた瞬間に、記録が古い面のものになる場合があると気づいた
直した
先頭に `v=1` を書き、知らない版なら全部捨てる。版が無い保存データは v1 として読む
版を上げるのは「面の中身を変えたとき」
バグ直しで面が変わらないなら、版は上げません。同じ番号の面が変わるときだけ上げます。上げると古い記録は消えますが、意味の違う記録が残り続けるよりましです。
壊れた保存データで落ちない
保存データは簡単に壊れます。更新の途中、別の版のゲーム、手で書き換えた人。壊れていても落ちないことを先に決めておくと、あとが楽です。
方針は「読めた分だけ使う」。読めない部分は、黙って捨てます。
foreach (var part in data.Split(';'))
{
var pair = part.Split('=');
if (pair.Length != 2) continue; // 壊れている部分は捨てる
if (!int.TryParse(pair[0], out int level)) continue;
if (!int.TryParse(pair[1], out int moves)) continue;
if (level < 1 || level > TotalLevels || moves <= 0) continue; // ありえない値も捨てる
...
}
テストは、わざと壊した文字列を渡します。
var store = new MemoryProgressStore { Data = "1=8;こわれた;=;3=;x=y;2=11;99999=3;4=-5" };
var progress = new Progress(store);
Assert.That(progress.BestMoves(1), Is.EqualTo(8)); // 読めた分は使う
Assert.That(progress.BestMoves(3), Is.EqualTo(0)); // 読めない部分は捨てる
Assert.That(progress.IsCleared(99999), Is.False); // 範囲外も捨てる
⚠️ 保存データは「信用できない入力」として扱う
自分のゲームが書いたものでも、読むときは外から来た文字列と同じです。数字だと決めつけない・範囲を確かめる・足りなくても進む。ここを守ると、保存まわりのクラッシュ報告が最初から発生しません。
保存先は「そのブラウザの中」
Unity の PlayerPrefs は、Web ビルドではブラウザの保存領域(IndexedDB)に入ります。ここは覚えておかないと、期待がずれます。
残る
残らない
端末をまたいで残したいなら、保存先はサーバーになります。2本目のゲームでは、ランキングだけサーバーに置きました(誰でも見える記録なので、そちらが自然です)。3本目の自己ベストは、自分だけのものなので端末に置いています。
本番で、保存して読み直すところまで確かめた
書いたら、公開したものでそのまま確かめます。スマホの見た目のまま面1をクリアし、ページを読み込み直して、記録が残っているかを見ました。

errors: なし
まとめ
- 保存するのは作り直せないものだけ。盤面は番号から作れるので保存しない
- 自動生成の面を保存と組み合わせるなら、版を書く。面の中身を変えたら版を上げて、古い記録は捨てる
- 保存データは信用できない入力として読む。読めた分だけ使い、落ちない
- Web の
PlayerPrefsはそのブラウザの中。端末をまたぐ記録が要るならサーバー - 公開したもので、クリア → 読み込み直しまで確かめる
FAQ
よくある質問
ゲームの進行は何を保存すればいいですか?
作り直せないものだけです。このパズルでは面の番号と自己ベスト(手数)だけを保存しています。盤面は面の番号から毎回同じものが作られるので、保存する必要がありません。
Unity の PlayerPrefs は Web でどこに保存されますか?
ブラウザの保存領域(IndexedDB)です。そのブラウザ・そのサイトの中にだけ残ります。別の端末や別のブラウザには引き継がれず、サイトデータを消すと一緒に消えます。
保存データが壊れていたらどうしますか?
読めた部分だけ使って、読めない部分は捨てます。例外で落ちるより、記録を一部忘れるほうがましだからです。壊れた文字列を渡すテストを置いて、落ちないことを見張っています。
面を自動生成しているとき、保存で気をつけることは?
生成の中身を変えると、同じ番号でも違う面になります。古い自己ベストがそのまま残ると、意味のない記録になります。保存データに版を書いておき、版が違えば捨てるようにしました。
この記事は役に立ちましたか?
NEXT
あわせて読む
Unity WebGLゲームにランキングを付ける(不正対策と送る条件)
ブラウザで動くゲームから送られる記録は、いくらでも書き換えられます。弾くのではなく「ありえない値は保存して無効にする」設計と、送るのは自己ベスト更新時だけという条件、EventSystem が無いシーンで名前を入力させる方法を、実装と実測つきで残します。
自動生成したパズルが必ず解ける作り方(逆再生+ソルバーで難しさも揃える)
箱押しパズルの面を自動生成しました。クリア状態から巻き戻して作れば必ず解けますが、それだけでは難しさが揃いません。1回引くたびに解かせて狙いの手数に合わせる方法と、狙いが外れ続けた原因、探索が重すぎて A* に変えた経緯を、実測つきで残します。
UI ToolkitだけでUnityのWebゲームを作る(uGUIを外して分かったこと)
3本目のゲームで uGUI をプロジェクトから外し、UI を UI Toolkit だけで作りました。EventSystem が使えない問題、シーンに設定を保存できない問題、Web ビルドだけ出るエラー、テーマが無いと文字が出ない話を、踏んだ順に残します。