ブラウザで遊ぶパズルに、進行の保存を入れました。保存するものはこれだけです。
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はそのブラウザの中。端末をまたぐ記録が要るならサーバー - 公開したもので、クリア → 読み込み直しまで確かめる