前回の記事で JSON 形式の簡単なセーブ・ロードクラスを公開しましたが、凝ったゲームでは以下の問題が出てくるかもしれません。
- Dictionary も使えるようにしたい
- JSON 形式だと、ユーザーにもデータがまる見えなので、なんとかしたい
- ユーザーがデータを改変した場合、検知できるようにしたい
これらも可能なセーブロードクラスを紹介します。
使い勝手はほとんど jsonSaveLoad と同じです。
ダウンロード(リポジトリ)
使い方
今回も SampleScene.unity にて確認できます。
厳密なエラー情報を返せるようにしたため、jsonSaveLoad とは多少引数が異なります。
保存するクラスについて
クラスは Serializable 属性をつける必要があります。
JSON 形式とは違い、Dictionary をメンバに含めることも可能です。
[System.Serializable] public class TestData { public int ID; public string Message; public Dictionary<string, int> StageCounter; }
リファレンス
binarySaveLoad.Save(string filename, object data)
クラスの中身をファイルに保存します。
filename .. ファイル名
data .. 保存したいクラス(のインスタンス)
戻り値 .. eResult.Success(成功)、それ以外はエラー
[System.Serializable] public class TestData { public int ID; public string Message; } TestData savedata = new TestData() { ID = 10, Message = "TestData", }; binarySaveLoad.Save("savedata", savedata);
binarySaveLoad.Load<T>(string filename, out T data)
指定したファイルからクラスデータを取得します。
filename .. ファイル名
data .. 任意のセーブデータクラスを指定
戻り値 .. eResult.Success(成功)、それ以外はエラー
binarySaveLoad.Load("savedata", out loaddata); string text = $"ID = {loaddata.ID}{Environment.NewLine}" + $"Message = '{loaddata.Message}'{Environment.NewLine}";
binarySaveLoad.Delete(string filename)
保存したファイルを削除します。
filename .. ファイル名
binarySaveLoad.Delete("savedata");
binarySaveLoad.IsZipArchive
true にすると、セーブデータを ZIP 圧縮します。
(セーブデータが大きくなりすぎるようであれば、使ってもいいかも)
binarySaveLoad.IsZipArchive = true;
binarySaveLoad.IsSimpleEncryption
true にすると、セーブデータを暗号化します。
暗号化ロジックについては、テスト用の簡単なものだけです。
より複雑な暗号化が必要であれば、次に紹介するユーザーメソッドの自作をオススメします。
binarySaveLoad.IsSimpleEncryption = true;
binarySaveLoad.UserEncrypt(byte[] data)
binarySaveLoad.UserDecrypt(byte[] data)
暗号化・複合化ルールを自分で定義することができます。
あまり重い処理にするとセーブ・ロードの時間にも影響するので、程ほどに。
binarySaveLoad.UserEncrypt = (data) => { for (int i = 0; i < data.Length; i++) data[i] += i % 256; }; binarySaveLoad.UserDecrypt = (data) => { for (int i = 0; i < data.Length; i++) data[i] -= i % 256; };
技術解説
今回はバイナリ形式のため、パッと見でデータを解析・変更することが困難です。
もちろん、どんなに複雑なデータ形式にしても解析不可能にはなりませんが、「これは解析する気が起こらないな…」とウンザリさせるだけでも、大きな効果が期待できます。
例:JSON 形式(わかりやすく、素人でも改変可能)
{"ID":10,"Message":"TestData","Flag":true,"Test":1,"Stage":[1,2,3,4,5]}
例:バイナリ形式(素人はまず手を出せない)
変換には BinaryFormatter の Serialized メソッドを使っています。
また、バイナリファイルの内容から4バイトのチェックサムを作成し、内部に保持しておきます。
こうすることで、例えバイナリを解析し、特定の値を変更(チート)したとしても「チェックサムと合わないからこのデータはおかしい」と判定され、読み込みに失敗します。
チェックサムが合わない理由として、セーブが不完全だった場合もあり得ます。
チートとは限らない事に注意してください。
バイナリ形式+チェックサムでほとんどの人がチート解析を諦めると思いますが、ZIP 圧縮や暗号化で更に解析を困難にすることも可能です。
バイナリのデメリットとしては、ファイルの保存サイズが大きくなる事です。
が、余程巨大なクラスでなければパフォーマンスの差は出ないと思います(たぶん)。