AIプログラムとかUnityゲーム開発について

探索や学習などを活用したAI系ゲームを作りたいと思います。

ゲームデータのセーブ

PlayerPrefsと同じような使い方で独自クラスもセーブできる機能実装【Unity】【セーブ】【Json】 | Unity開発Tips
上記のライブラリをテスト中

テスト用のサンプルにデータの削除を追加して削除されるか確認

        //セーブデータ
//        SaveData.SetClass<Player> ("p1", new Player ());
//        SaveData.Save ();

        //ロードデータ
        Player getPlayer = SaveData.GetClass<Player> ("p1", new Player ());
 
        Debug.Log (getPlayer.name);
        Debug.Log (getPlayer.items.Count + "こアイテムを持ってます");
        Debug.Log (getPlayer.items[0] + getPlayer.items[1] + getPlayer.items[2] );

SaveData.Remove("p1");
SaveData.Save();

あれ削除されてない?

それもそのはず。new Player()で作られるデータは、セーブしていたデータと同一なので、
GetClassしたときに、キー"p1"のバリューは空になっているのに、次の引数であるnew Player()の結果が再代入されているのですw
Playerクラスとのコンストラクタを、最初はアイテム何も持ってないに変えれば、Removeされたデータのアイテムが空になります。
なんかちょっとわかりにくかった。


データの保存先は、
Application.persistentDataPath
に入っているパス

データ形式JSONで、確認してみると
サンプルだと、

{"keys":["i","p1"],"values":["10","{\"hp\":10,\"atk\":100.0,\"name\":\"クラウド\",\"items\":[\"ポーション\",\"エーテル\",\"エリクサー\"]}"]}

のような形式で保存されていた。

暗号化対応

暗号化の解説もあるので、どうせなら暗号化してみよう。
セーブ機能の実装方法まとめ 使い勝手の良いセーブを実装する【Unity開発】 | Unity開発Tips


動いた。Crypt.csもAsset内にダウンロードして、

SaveData.csの
Save()に
追加↓
dictJsonString = Crypt.Encrypt(dictJsonString);

Load()に
追加↓
string decrypted = Crypt.Decrypt(sr.ReadToEnd());
変更↓
var sDict = JsonUtility.FromJson>(decrypted);

これでデータの暗号化・暗号化解除ができてるみたい。

f:id:yasu9780:20170223170835p:plain

バイナリ保存対応

これって、暗号化プログラムを読むと、
暗号化:暗号化したバイナリをBase64でテキストにして保存。
復号:Bas64テキストをバイナリに変換して復号してテキスト化

これって暗号化したバイナリをわざわざテキストにして保存して、
テキストを読み込んで、またバイナリにして復号してるけど、
ならバイナリはバイナリで保存した方がファイル小さくなるんじゃないの?

am1tanaka.hatenablog.com
↑バイナリ保存方法

バリナリ保存できないか試してみる

バイナリ化できたっぽい


SaveData.csのLoad()を

public void Load() {
 if (File.Exists(path + fileName) && saveDictionary != null)
 {
    string decrypted = Crypt.DecryptB(File.ReadAllBytes(path + fileName));
    var sDict = JsonUtility.FromJson<Serialization<string, string>>(decrypted);
    sDict.OnAfterDeserialize();
    saveDictionary = sDict.ToDictionary();
 }
            else { saveDictionary = new Dictionary<string, string>(); }
}

Save()を

public void Save() {
            var serialDict = new Serialization<string, string>(saveDictionary);
            serialDict.OnBeforeSerialize();
            string dictJsonString = JsonUtility.ToJson(serialDict);
            File.WriteAllBytes(path+fileName, Crypt.EncryptB(dictJsonString));
}

Encrypt.csを、
バイナリで読んで復号テキスト返す
テキストを暗号化してバイナリ返す
の二つ関数を追加
元々バイナリをテキストにしてる部分をやめるだけ
もっとも、バイナリといっても、数値1234は文字なら4文字だけど(asciiなら4byte)、
16bitの値なら2byteで表せる。みたいな方向のバイナリ化をしないとデータが小さくなるってことはあまりないと思う。
圧縮とかすれば別だろうけど。

    public static byte[] EncryptB(string text)
    {
        RijndaelManaged aes = new RijndaelManaged();
        aes.BlockSize = 128;
        aes.KeySize = 128;
        aes.Padding = PaddingMode.Zeros;
        aes.Mode = CipherMode.CBC;
        aes.Key = System.Text.Encoding.UTF8.GetBytes(AesKey);
        aes.IV = System.Text.Encoding.UTF8.GetBytes(AesIV);

        ICryptoTransform encrypt = aes.CreateEncryptor();
        MemoryStream memoryStream = new MemoryStream();
        CryptoStream cryptStream = new CryptoStream(memoryStream, encrypt, CryptoStreamMode.Write);

        byte[] text_bytes = System.Text.Encoding.UTF8.GetBytes(text);

        cryptStream.Write(text_bytes, 0, text_bytes.Length);
        cryptStream.FlushFinalBlock();

        byte[] encrypted = memoryStream.ToArray();

        Debug.Log("encrypted:" + System.Convert.ToBase64String(encrypted));

        return encrypted;
    }

    public static string DecryptB(byte[] encrypted)
    {
        RijndaelManaged aes = new RijndaelManaged();
        aes.BlockSize = 128;
        aes.KeySize = 128;
        aes.Padding = PaddingMode.Zeros;
        aes.Mode = CipherMode.CBC;
        aes.Key = System.Text.Encoding.UTF8.GetBytes(AesKey);
        aes.IV = System.Text.Encoding.UTF8.GetBytes(AesIV);

        ICryptoTransform decryptor = aes.CreateDecryptor();

//        byte[] encrypted = System.Convert.FromBase64String(cryptText);
        byte[] planeText = new byte[encrypted.Length];

        MemoryStream memoryStream = new MemoryStream(encrypted);
        CryptoStream cryptStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);

        cryptStream.Read(planeText, 0, planeText.Length);

        Debug.Log("decrypted:" + System.Text.Encoding.UTF8.GetString(planeText));

        return (System.Text.Encoding.UTF8.GetString(planeText));
    }