Unityでセーブデータを暗号化してSerialize保存 ~その2~


UnityでセーブデータをSerialize保存する 〜現状〜
↑ 現状をまとめた


以前の記事の続き。もう少し調べみた☆

C#でのSerialize保存

C#では標準で便利なSerialize保存出来るライブラリが提供されている。

  • BinaryFormatter・・・オブジェクトをバイナリ化してシリアライズ保存出来る。Unity/iOSで動かないと言われているが・・・?
  • DataContractSerializer・・・オブジェクトをXML形式でシリアライズ保存出来る
  • DataContractJsonSerializer・・・オブジェクトをJson形式でシリアライズ保存出来る(まじかΣ(゚д゚lll))

Jsonのシリアライズ化標準で出来るじゃん_| ̄|○まぁ、LitJsonと同じっちゃ同じだけどね(;^_^A
DataContractSerializerとDataContractJsonSerializerは前回やった内容と一緒なのでスルー(XMLはデータ量も多いし暗号化も出来ないしね)。
BinaryFormatterをちょい調べてみた。

BinaryFormatter

そもそもC#でシリアライズ保存する場合はBinaryFormatterを使うのが普通だと思う。じゃあなんでそれに触れなかったかって、ネットで調べてたら以前にBinaryFormatterはiOSでは動かないという記事を見かけたため。実際に自分で試したわけではない。
ということで、今現在の情報でちょっと詳しく調べたところ、なんかBinaryFormatterでうまくいくかもしれない(笑)

Unity4 5 iOSでBinaryFormatter が使えない?
FileStream + BinaryFormatter from C# to iOS doesn't work? - Unity Answers

こちらの記事によると、iOSの場合はEnvironment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes")を使用すればエラーが出ることなくいけるとのこと。
コードにするとこんな感じ。

SceneSample.cs
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class SaveData
{
    public int Id;
    public string Name;
    [NonSerialized()]
    public float TemporaryData;
}

public class SceneSample : MonoBehaviour
{
    private static readonly string SavePath = Application.dataPath + "/save.bytes";

    void Start()
    {
        // iOSでは下記設定を行わないとエラーになる
#if UNITY_IPHONE
        Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");
#endif

        ///// 保存 /////////////////////////////////////
        {
            SaveData obj = new SaveData();
            obj.Id = 1;
            obj.Name = "tempura";
            obj.TemporaryData = 3.5f;

            using (FileStream fs = new FileStream(SavePath, FileMode.Create, FileAccess.Write))
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(fs, obj);
            }

            Debug.Log("[Save]Id:" + obj.Id);
            Debug.Log("[Save]Name:" + obj.Name);
            Debug.Log("[Save]TemporaryData:" + obj.TemporaryData);
        }

        ///// 読み込み /////////////////////////////////////
        {
            // 読み込み
            SaveData obj = null;
            using (FileStream fs = new FileStream(SavePath, FileMode.Open, FileAccess.Read))
            {
                BinaryFormatter bf = new BinaryFormatter();
                obj = bf.Deserialize(fs) as SaveData;
            }

            Debug.Log("[Load]Id:" + obj.Id);
            Debug.Log("[Load]Name:" + obj.Name);
            Debug.Log("[Load]TemporaryData:" + obj.TemporaryData);
        }
    }
}

うまくいきますね~(笑)

Unityの.NET Frameworkのバージョン

BinaryFormatterを使う上でもう1つ問題だったのがUnityの.NET Frameworkのバージョン。BinaryFormatterは.NET Framework 4.5から使用可能。
Unityの標準での.NET Frameworkのバージョンは3.5。んで、どこまで対応されているかというと・・・

ん?なんだ、よくわからん(笑)

Unity5でC#5&.NET4.5まで対応されるらしいが、まだ対応されてないのか??
.NET4.0まで使えるのは確認出来たが、4.5はどうなんろう・・・

暗号化

BinaryFormatterはデータをバイナリ化してシリアライズ保存するので便利だが、バイナリ化したとはいえ中身を確認すればプレーンなテキストは丸分かりである。まぁ、バイナリなんでパッと見分かりづらいし、ここらへんのセキュリティ周りの話しは宗教戦争起こりそうなのでw、どうするかはそれぞれの環境で考える必要があるかと。
個人的には重要なデータだけ暗号化して、それ以外はこのままでいいかなぁ、と思う。最終的にはクライアントは不正されるものと思って、サーバ側で対応すべきだと思うし。

UnityのC#で使えない高機能を使う?

この記事を作る過程で調べてて面白い記事を見つけた。

Unity(ゲームエンジン)上で async/await

UnityはDLLの参照もできるので、コアロジックをUnityプロジェクトから完全に分離して、DLL化してから、Unityプロジェクト上にコピーすればコンパイラーの制限はかかりません。普通にC# 6.0が使えます。

iOSの場合、.NETやJavaのような仮想マシンコード実行が認められていないので、AOT(Ahead Of Time)というコンパイル方法で、事前にネイティブ コード化してアプリ パッケージ化します。こいつが曲者というか、古いバージョンのMonoでは制限がきつくて、いろいろなコードが実行時エラーになって困ります。

そしたら、「コンパイラーの差し替えして、Unityプロジェクト側でもC# 5.0/6.0使えるよ」などと教えていただきまして。
なるほど。その手が一応あるのか…
ちなみに、これ、「IL2CPPがまともに動くなら」という前提がかかります。IL2CPPは、公称では「任意のILコードを実行できる」となっているので、コンパイラーを差し替えてもちゃんと動くはず。
IL2CPPさえまともなら… (ちなみに、今開発中のプロジェクトはいまだIL2CPPで動かず。)

これはすごい。ただちょっと現状、すぐに対応は出来なさそうだけど、勉強になる。
このサイト、C#で一番良く見てるサイトだけど、やっぱすげえわ・・・

最後に

BinaryFormatter、まだ実機で試してないけど、いける気がするなぁ。実環境で使ってる人もいるっぽいし。
今度、実機で試してみよう。