【ES3】EasySave3を使ってみる

Unityプロジェクトのセーブデータ管理としてEasySave3を利用してみることにしました。

使用方法については他の記事がわかりやすいので省略。

主に使用してる途中に気づいたことがあれば追記していきます


目次



セーブするクラスはデフォルトコンストラクタが使える状態にすること

[System.Serializable]
public class Hoge
{
    [SerializeField] private int _x;

    public Hoge(int x)
    {
        _x = x;
    }
}

このようにコンストラクタに引数があると Serializable がついているクラスであろうと 保存が出来ない ので注意。

ES3Serializable を使用すればプロパティも保存可能

public class Hoge
{
    [ES3Serializable]
    public int X { get; private set; }
}

ES3Serializableアトリビュートを使用すればプロパティの状態でセーブ可能。

プロパティしかないクラスが多いので助かった。


※ private set などの、 set 構文が無いと「値の設定ができないよ!」と怒られるので注意

<追記>

継承先のクラスを保存していて、継承元のプロパティを保存したいケース。


class Parent
{
    [ES3Serializable] public int Test { get; private set; }
}

class Hoge : Parent
{
}

....

Save(new Hoge());

これはHogeのLoad時に失敗します。
理由としてはHogeを読み込んでTestをセットする時にアクセス制限があるからです。

protected set と変えなきゃいけないです

(意外な落とし穴)

Nullableは保存できない

f:id:toshizabeth:20210529171422p:plain

public int? _x;

のようなもの。保存はできるが読み込み時に NotSupportedException が出る。

Unity2021 で EasySave3 の Editor Window が開けない。エラーが出る

現状(2021/5/30)、フォーラムでも調査中のようです

problem with new version unity - Forums | Moodkie Interactive


追記: 現状のエラーについてフォーラムを纏めてみる

Error when trying to add the manager to scene - Forums | Moodkie Interactive

現在の最新バージョン 2021.1.9 でも同じ問題が起きています

→ 2020.3.10f1 にダウングレードすると表示されるようになりました。(アップ、ダウンしたあとはLibraryフォルダを消してリフレッシュ推奨


MonoBehaviourをSave、Loadするには?

EasySaveを使用するにあたって「MonoBehaviourの保存....? 」というのが気になってました。 気になってたけどスルーしてた。


使用するととても便利な機能だということがわかった。


GameObjectの保存

まずは ES3 Reference Mgr が必要なのでシーンに追加。

f:id:toshizabeth:20210530183000p:plain

(今回はAutoSaveについては省く) f:id:toshizabeth:20210530183013p:plain

この状態であとは通常と同じように GameObject を Save に渡す。

ここで面白いのが

  1. GameObejct 自体を渡すとそのObject全体の情報が書き込まれる。

  2. クラスのインスタンスを渡すと、そのインスタンスの情報だけが書き込まれる。


つまり、特定のGameObject全体を保存したいなら XXX.gameobject というふうにSaveに渡せばOK

というかこの使い方はすごく便利。

RectTransformの回転値なども保存できるので一々復元処理がいらない。

逆に特定のObjectの中のComponentだけを保存/読み込みしたい場合はgameobjectでは無く、そのインスタンス自身を渡せば良い


あとはLoadしてやる。

GameObjectのLoadは二種類あり、

  1. Load("Path", "key");

  2. LoadInto("Path", "key", gameobject );


の二種類。

1 の Load は「 保存したときと同じGameObjectがシーン内にあるならばその対象に対して値が上書きされる。」というもの

2 の LoadInto は「引数のgameobjectに保存したときの値が上書きされる」というもの


どちらの使い方をしても良いけど、1のほうが面倒くさいことをしなくて良さそうではある。


気になるのが「どうやってGameObjectを検索して上書きしてるの?」という話なわけで。

ES3RefernceMgrを見ると Reference Count というものがある f:id:toshizabeth:20210530184042p:plain

これは「アタッチされた時」、「Refreshボタンが押された時」、「起動された時」に現在のシーン内のGameObjectを全検索してオブジェクトのID情報をマップしてくれてる


保存したセーブデータを除くと "transformID" や "goID" が保存されてる。

f:id:toshizabeth:20210530184454p:plain

ここから検索して引っかかったGameObjectに対して値を上書き


逆に、見つからないとGameObejctを勝手に作ってManagerがあるシーンの直下に作成されます

基本的なGameObjectの保存と読み込みはこれだけでできる。便利

ただ便利だからといって全て保存し続けるとデータ容量が大きくなりすぎてその分データの保存読み込みのエラー率も上がりますね

使い所大事


GameObjectの情報以外が保存されない問題

そのまま gameObjectを指定して読み込んでもGameObjectに必要な情報+サポートされてる型しか保存されない

(↓ Transformとか) f:id:toshizabeth:20210530213646p:plain


自分で作成したクラスも含めたい場合はその情報をスクリプトで定義する必要がある。

一々手で作るのは面倒くさいので用意してあるEditor機能で省略しましょう


Tools > EasySave3 > Types

ここで情報を一緒に保存したいComponent名を入れて選択

あとは保存したいものを選択してスクリプト作成

f:id:toshizabeth:20210530213830p:plain

以下の形で作られるのでこれで準備完了。

f:id:toshizabeth:20210530214014p:plain

あとは通常通りSaveする


f:id:toshizabeth:20210530214318p:plain

PlayerControlが見事保存されてますね。子供のデータもちゃんと入ってる。OK

復元(Load)も見事にしてくれるので文句なし!


何でもかんでも保存したら容量すごいので必要最低限にしましょう

(個人的にスクリプト作らなくても行けそうな気もしてる... いや、必要だからの機能なんだろうけど引っかかってる..)


別シーンのMonobehaviourの保存

EasySave3Managerがついているシーンとは 別シーン の GameObejct を保存 という使い方をしたい場合。

(マルチシーン開発している時)


1シーン内でSaveもLoadも完結するなら特に以前の使い方で良いが、保存したいObjectがあるシーンがManagerと異なる場合は保存は問題なくできるが読み込み時に「GameObjectIDの検索に失敗する」ので注意。

つまり解決策は保存したいObjectがあるシーンにManagerも存在する必要がある。


以下のように各シーンに一つManagerを生成してください(アクティブなシーンにManagerが存在していると Add To Scene で追加できないので、一度他のシーンを終了させてから右クリック>生成するように)

f:id:toshizabeth:20210530201734p:plain



その他

MVRPで実装しており「データクラスで必要なものを保存して、それを読み込めば良くない?」と思い、Modelだけ保存するようにしています。

(データクラスを分けておくのはこういうときも便利)


ES3はクラスをそのまま保存、読み込みができるのでModelの必要な部分を取って保存してみました。


ローグライクゲームを作成していて、タイル情報も保存するようにしたところ...

f:id:toshizabeth:20210529164951p:plain


29705行

3階層分は常に表示する特殊なダンジョンの仕組みにしているので単純に三倍のデータ量ですごいことに....

容量的には 512KB。


問題ないと捉えられる範囲だけど、今後広くなる可能性も高いし他にも保存したいデータたくさんある(落ちてるアイテムとか)。

最適化を考えよう。

f:id:toshizabeth:20210529165228p:plain

(Posは実行時に数え直せばいいので Pos と Index を消すだけで大幅に減りそう )