【Unity】PlayerPrefs に任意の型を保存する拡張

こんにちは uni928 です。
皆さんは PlayerPrefs を使用する機会はありますか？

~~私はほとんどありませんが~~
プログラミング初心者の方には使いやすい
機能だと思います。

Unity を習得する際も
PlayerPrefs で練習するのは
多くの人が通る道ではないでしょうか？
~~そうでもないでしょうか？~~

しかしながら PlayerPrefs は
string int float しか保存する事はできません。

そこで PlayerPrefs に
任意の型を保存する拡張を
提供したいと思います。

Unity 習得中の人も
是非導入して頂けると幸いです。

***

[https://drive.google.com/file/d/1KFfQqLLjSHW8_n1Mr5V9bPV-a_K7ye31/view?usp=sharing](https://drive.google.com/file/d/1KFfQqLLjSHW8_n1Mr5V9bPV-a_K7ye31/view?usp=sharing)

unitypackage 版は
上記の URL からダウンロードして下さい。

SephirothPlayerPrefsExtension.cs
以外のファイルは削除してかまいません。
HowToUse.pdf 等は容量が大きいため
削除する事を推奨します。

***

では仕組みを作成していきます。

SephirothPlayerPrefsExtension.cs を作成して
下記の内容にして下さい。

```Csharp:SephirothPlayerPrefsExtension.cs
using System.Security.Cryptography;

namespace SephirothTools
{
    public static class SephirothPlayerPrefsExtension
    {
        private static readonly System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        private static readonly System.IO.MemoryStream ms = new System.IO.MemoryStream();

        public static void Save<T>(string key, T value)
        {
            ms.Position = 0;
            ms.SetLength(0);
            formatter.Serialize(ms, value);
            UnityEngine.PlayerPrefs.SetString(key, System.Convert.ToBase64String(Encrypt(ms.ToArray())));
        }

        public static T Load<T>(string key, T defaultValue = default)
        {
            string loadValue = UnityEngine.PlayerPrefs.GetString(key, "");
            if (string.IsNullOrEmpty(loadValue))
            {
                return defaultValue;
            }

            byte[] bytes = Decrypt(System.Convert.FromBase64String(loadValue));
            ms.Position = 0;
            ms.Write(bytes, 0, bytes.Length);
            ms.Position = 0;

            return (T)formatter.Deserialize(ms);
        }

        private const string AesIV = @"kgificpplgodckdi"; // Declare a random string of 16 single-byte characters.
        private const string AesKey = @"lfsaokfdsjhahfppjdfjshfsdgssdfas"; // Declare a random string of 32 single-byte characters.
        private const int KeySize = 256;
        private const int BlockSize = 128;

        private static byte[] Encrypt(byte[] byteValue)
        {
            return GetAes().CreateEncryptor().TransformFinalBlock(byteValue, 0, byteValue.Length);
        }

        private static byte[] Decrypt(byte[] byteValue)
        {
            return GetAes().CreateDecryptor().TransformFinalBlock(byteValue, 0, byteValue.Length);
        }

        private static AesManaged GetAes()
        {
            var aes = new AesManaged();
            aes.KeySize = KeySize;
            aes.BlockSize = BlockSize;
            aes.Mode = CipherMode.CBC;
            aes.IV = System.Text.Encoding.UTF8.GetBytes(AesIV);
            aes.Key = System.Text.Encoding.UTF8.GetBytes(AesKey);
            aes.Padding = PaddingMode.PKCS7;
            return aes;
        }
    }
}
```

PlayerPrefs.SetString("key1", value);
の代わりに
SephirothTools.SephirothPlayerPrefsExtension.Save＜string＞("key1", value);
を使用します。

PlayerPrefs.GetString("key1");
の代わりに
SephirothTools.SephirothPlayerPrefsExtension.Load＜string＞("key1");
を使用します。

多くの型を保存できるため
SephirothTools.SephirothPlayerPrefsExtension.Save＜List＜int＞＞("key2", new List<int>(){1, 2, 3});
List＜int＞ loadList = SephirothTools.SephirothPlayerPrefsExtension.Load＜List＜int＞＞("key2");
のような記述もできます。

Save＜T＞ Load＜T＞ の T の部分に
保存する型を入れる事で使用できます。

暗号化に関しては
直接書いてしまっているため
逆コンパイルに対応できません。
おまじない程度の認識でお願いします。
暗号化しても速度がほとんど変わらないため
やる意味は薄くても記述する価値があります。

***

あとがき

以上が PlayerPrefs に
任意の型を保存する仕組みになります。

PlayerPrefs の代わりに
SephirothTools.SephirothPlayerPrefsExtension.Save
SephirothTools.SephirothPlayerPrefsExtension.Load
を使用するだけなので
使用するのは簡単だと思います。

この仕組みが
皆さんの開発の助けになれば幸いです。
閲覧ありがとうございました。