(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

(:3[kanのメモ帳]

Easy Saveを使ってるうちに遭遇するかもしれない、いくつかのエラーとその対処法【Unity】【アセット】【Easy Save】【トラブルシューティング】


このエントリーをはてなブックマークに追加

この記事でのバージョン
Unity 2018.4.4f1
Easy Save 3.1.3


はじめに

以前、簡単にセーブ&ロード&暗号化が実装できるEasy Saveというアセットを紹介しました。

//セーブ
ES3.Save<int>      ("IntKey",       100);
ES3.Save<bool>     ("BoolKey",      false);
ES3.Save<string>   ("StingKey",     "Text");
ES3.Save<Transform>("TransformKey", transform);

//ロード
int    num  = ES3.Load<int>   ("IntKey");
bool   flag = ES3.Load<bool>  ("BoolKey");
string text = ES3.Load<string>("StingKey");
ES3.LoadInto<Transform>("TransformKey", transform);


今回はこのEasy Saveを使ってるうちに遭遇するかもしれない、いくつかのエラーとその対処法

についてまとめた感じの記事です!



なお、現状Easy SaveはES2とES3がありますが、ES3の話ですのであしからず。

(ES2でも同様のエラーが出る、同じ対処法で直るという事もあるかもしれません)


保存されていないデータのロード

Easy Saveは保存されていないKeyでロードをしようとすると、エラーが出ます。

//StingKeyで保存
ES3.Save<string>("StingKey", "Text");

//StingKey2でロード(データがないのでエラー)
string text = ES3.Load<string>("Sting2Key");

KeyNotFoundException: Key "Sting2Key" was not found in file "/Users/~~~~/SaveData.es3". Use Load(key, defaultValue) if you want to return a default value if the key does not exist.

ES3Reader.Read[T] (System.String key) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3Reader.cs:158)
ES3.Load[T] (System.String key, ES3Settings settings) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:269)
ES3.Load[T] (System.String key) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:240)

f:id:kan_kikuchi:20190727115717j:plain


こんな時はdefaultValueを使って、データが保存されていない時の値を指定するか、

//StingKey2でロード、まだ保存されていない時は"Default"を取得
string text = ES3.Load<string>("Sting2Key", defaultValue:"Default");


ExistKeyを使ってデータが保存されているか確認してからロードすると良いでしょう。

//先にデータが保存されていない時の値をtextに設定
string text = "Default";

//Sting2Keyでデータが保存されていればそれをロード
if (ES3.KeyExists("Sting2Key")) {
  text = ES3.Load<string>("Sting2Key");
}


同様に、データが一つもセーブされてない状態でロードしようとした場合もエラーがでます。

FileNotFoundException: File "/Users/~~~~/SaveData.es3" could not be found.

ES3.Load[T] (System.String key, ES3Settings settings) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:268)
ES3.Load[T] (System.String key) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:240)

f:id:kan_kikuchi:20190727115334j:plain


これについても、先程のdefaultValueかExistKeyを使う事で対処出来ます。


セーブ時とロード時で型が違う

Easy Saveはセーブ時とロード時で型が違うと、エラーが出ます。

//intで保存
ES3.Save<int>("StingKey", "Text");

//stringでロード(型が違うのでエラー)
string text = ES3.Load<string>("StingKey");

InvalidCastException: Specified cast is not valid.

ES3Types.ES3Type_int.Write (System.Object obj, ES3Writer writer) (at Assets/Plugins/Easy Save 3/Scripts/Types/Primitive Types/ES3Type_int.cs:18)
ES3Writer.Write (System.Object value, ES3Types.ES3Type type, ES3+ReferenceMode memberReferenceMode) (at Assets/Plugins/Easy Save 3/Scripts/Writers/ES3Writer.cs:148)
ES3Writer.WriteProperty (System.String name, System.Object value, ES3Types.ES3Type type, ES3+ReferenceMode memberReferenceMode) (at Assets/Plugins/Easy Save 3/Scripts/Writers/ES3Writer.cs:211)
ES3Writer.Write[T] (System.String key, System.Object value) (at Assets/Plugins/Easy Save 3/Scripts/Writers/ES3Writer.cs:86)
ES3.Save[T] (System.String key, System.Object value) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:30)

f:id:kan_kikuchi:20190727120200j:plain


残念ながらEasy Saveに保存されているデータの型を判定するメソッドとかはないので、

根本的な解決方法はないのですが、

セーブデータ周りの専用クラスを作り、色んな所でセーブやロードの処理をしない

「定数クラスを作るなどしてKeyを一元管理し、重複しないようにする」

などの対処法が考えられます。


暗号化前後のデータ

Easy SaveはWindow->Easy Save 3から開けるウィンドウの


f:id:kan_kikuchi:20190430055639j:plain


SettingsのEncryption TypeをAESにし、Passwordに好きな文字列を入れると

ファイル全体を簡単に暗号化する事が出来ます。


f:id:kan_kikuchi:20190430060622j:plain


ただし、暗号化設定する前に保存したデータを

暗号化設定した後にロードしようとするとエラーが出ます。

FormatException: File is not valid JSON. Expected '{' at beginning of file, but found '�'.

ES3Internal.ES3JSONReader..ctor (System.IO.Stream stream, ES3Settings settings, System.Boolean readHeaderAndFooter) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3JSONReader.cs:39)
ES3Reader.Create (ES3Settings settings) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3Reader.cs:314)
ES3.Load[T] (System.String key, ES3Settings settings) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:265)
ES3.Load[T] (System.String key) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:240)

f:id:kan_kikuchi:20190727122548j:plain


なので、暗号化設定を変えた時はToolsのClear Persistent Data Pathで一旦データを削除しましょう。

f:id:kan_kikuchi:20190727123413j:plain


なお、暗号化していたものを暗号化しないようにした場合も一旦データを削除しないとエラーが出ます。

ArgumentException: Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.

System.Buffer.BlockCopy (System.Array src, System.Int32 srcOffset, System.Array dst, System.Int32 dstOffset, System.Int32 count) (at :0)
Mono.Security.Cryptography.SymmetricTransform.FinalDecrypt (System.Byte inputBuffer, System.Int32 inputOffset, System.Int32 inputCount) (at :0)
Mono.Security.Cryptography.SymmetricTransform.TransformFinalBlock (System.Byte
inputBuffer, System.Int32 inputOffset, System.Int32 inputCount) (at :0)
System.Security.Cryptography.CryptoStream.FlushFinalBlock () (at :0)
System.Security.Cryptography.CryptoStream.Dispose (System.Boolean disposing) (at :0)
System.IO.Stream.Close () (at :0)
System.IO.Stream.Dispose () (at :0)
ES3Internal.AESEncryptionAlgorithm.Decrypt (System.IO.Stream input, System.IO.Stream output, System.String password, System.Int32 bufferSize) (at Assets/Plugins/Easy Save 3/Scripts/ES3Crypto.cs:139)
ES3Internal.UnbufferedCryptoStream..ctor (System.IO.Stream stream, System.Boolean isReadStream, System.String password, System.Int32 bufferSize, ES3Internal.EncryptionAlgorithm alg) (at Assets/Plugins/Easy Save 3/Scripts/ES3Crypto.cs:165)
(wrapper remoting-invoke-with-check) ES3Internal.UnbufferedCryptoStream..ctor(System.IO.Stream,bool,string,int,ES3Internal.EncryptionAlgorithm)
ES3Internal.ES3Stream.CreateStream (System.IO.Stream stream, ES3Settings settings, ES3Internal.ES3FileMode fileMode) (at Assets/Plugins/Easy Save 3/Scripts/Streams/ES3Stream.cs:69)
ES3Internal.ES3Stream.CreateStream (ES3Settings settings, ES3Internal.ES3FileMode fileMode) (at Assets/Plugins/Easy Save 3/Scripts/Streams/ES3Stream.cs:56)
ES3Reader.Create (ES3Settings settings) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3Reader.cs:308)
ES3.Load[T] (System.String key, ES3Settings settings) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:265)
ES3.Load[T] (System.String key) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:240)

f:id:kan_kikuchi:20190727122806j:plain



Easy Save 3 Managerのロードタイミング

Easy SaveはデフォルトだとシーンにManagerが自動生成されます。

f:id:kan_kikuchi:20190727123608j:plain


これはSettingsのAuto Add Manager to Sceneからオフにする事ができますが、

f:id:kan_kikuchi:20190727123614j:plain


そうするともちろん、シーン上にManagerがなくなってしまうので、

適宜、Resourcesからロードして使うみたいな方法が考えられます。

f:id:kan_kikuchi:20190727125952j:plain
private void Awake() {
  //Managerのインスタンス作成
  Instantiate(Resources.Load("Easy Save 3 Manager"));
    
  //データを保存
  ES3.Save<string>("StingKey", "Text");
}


ただ、こうすると、暗号化設定をしている時、

かつ、2回目以降の起動時にエラーが出るようになります。

FormatException: File is not valid JSON. Expected '{' at beginning of file, but found '�'.

ES3Internal.ES3JSONReader..ctor (System.IO.Stream stream, ES3Settings settings, System.Boolean readHeaderAndFooter) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3JSONReader.cs:39)
ES3Reader.Create (ES3Settings settings) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3Reader.cs:314)
ES3.Load[T] (System.String key, T defaultValue, ES3Settings settings) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:306)
ES3AutoSaveMgr.Load () (at Assets/Plugins/Easy Save 3/Scripts/Auto Save/ES3AutoSaveMgr.cs:42)
ES3AutoSaveMgr.Awake () (at Assets/Plugins/Easy Save 3/Scripts/Auto Save/ES3AutoSaveMgr.cs:56)
UnityEngine.Object:Instantiate(Object)


エラー内容は暗号化前後のデータの時と同じですが、

このパターンだと、一旦データをリセットしても解決しません。


こんな時はAuto SaveのLoad EventをNoneにするとエラーがでなくなります。

f:id:kan_kikuchi:20190727132026j:plain


なお、Auto Saveに何も表示されない事もあるので、

f:id:kan_kikuchi:20190727132053j:plain


そういう場合はManagerのPrefabにあるManage Auto Save Settingsを押す

Auto Saveの設定が出来ます。

f:id:kan_kikuchi:20190727132107j:plain