【Unity】RPGツクール仕様のセーブ機能を実装する【Easy Save 3】

Unityには標準で「PlayerPrefs」というセーブ機能がついており、これを使うだけでも理論上は複雑なセーブ機能を作れます。理論上は。

が、RPGのように

  • 一度別のマップ(Scene)に移動してから、再度同じマップに戻ってきた際に変数を復元
  • 手動でセーブしていなければ、その変数は次回起動時に初期化
  • セーブスロットを分ける

これらを実装したい場合、PlayerPrefsだけでは困難を極めます。

例えば、

  1. 住民と2回話すとセリフパターンが変化
  2. 別のマップから戻ってきた際に、最初の会話に戻らず2回目のままにする
  3. プレイヤーが手動でセーブしない限り、これらはゲーム終了時に破棄される

などですね。

RPGは頻繁にマップ移動(=Sceneの移動)が生じるので、そのSceneで変化した値を柔軟に保存できている必要があり、いつでも復元できなければなりません。

建物、街、フィールド……RPGでは、頻繁にマップが切り替わる。

今回は、この処理を、Easy Save 3というアセットで実装してみます。名前の通り、セーブ関係の機能を簡単に実装できるアセットです。

有料かよという声も分かりますが、試しに一度自力で実装してみてください、僕はやりました。マジで地獄です。

頼れるところは頼って、本当に作りたい部分に集中しましょう。

Scene間で値を一時保存・復元する

ES3 Auto Saveを使う

別記事でも扱いましたが、Easy Save 3にはオートセーブ機能がついています。


Sceneの移動時に値を保存するだけなら、これで十分です。

しかし、次回起動時にも値が保存されている上に、セーブスロットの区分けもできていません。これらの処理を実装するために、一手間加えます。

スロットに分け、次回起動時に初期化

必要なScript

結論から先に述べます。以下のScriptを、適当なGame Objectにアタッチしてください。(使い方・何をやっているかの解説は後述します)。

なお、導入にはSingletonMonoBehaviourが必要です(別にシングルトンにする必然性は無いが、その際は全Sceneに手動でアタッチする必要があります)。
以下のScriptをインポートしてください(アタッチする必要はありません)。

加えて、Easy Save 3の設定を変える必要があります。Unity上部のメニューの

Window > Easy Save 3…> Auto Save

を開き、以下のように設定してください。

大事なのは赤枠部分なので、後は好きな設定でOKです。

最後に、保存対象にES3AutoSaveをアタッチし、ES3Typeで保存する変数を指定しましょう。この辺の解説は、過去記事に記載してあるのでそちらをどうぞ。

使い方

任意のタイミングで、以下のScriptを記述します。

SceneParameterManager.Instance.Save(スロット番号)

指定スロットへセーブします。すでに存在する場合は上書きします。
スロット番号を省略できます。

SceneParameterManager.Instance.Load(スロット番号)

指定スロットのデータをロードします。存在しない場合は何も起きません。
スロット番号を省略できます。

SceneParameterManager.Instance.SlotID

現在の操作対象スロットです。ロード・セーブ時にスロット番号を省略した場合はこちらが使われます。スロット番号を変えたい場合、このプロパティに直に値を代入してください。

これで、Sceneの切替時にオートセーブ対象が自動で保存され、ゲーム終了時に破棄され、Saveメソッドを呼び出すことでファイルに保存し、Loadメソッドで読み込むことができるようになります。

SceneParameterManagerを”最初の”Sceneに配置するのが面倒・忘れてしまう人は、うら干物さんの記事がとても参考になるので、活用してみてください。

スポンサードサーチ

Scriptの解説

以降はScriptで何をやっているかの解説なので、知りたい人だけ読んでください。

一連の流れ

まず、大まかに何をやっているのかを解説します。

  1. Sceneの切り替え前にオートセーブ
  2. Sceneの切り替え後にオートロード
  3. Saveメソッドで、オートセーブのデータをコピーして別名で保存
  4. ゲーム終了時にオートセーブを破棄

順に解説します。

Sceneの切り替え前にオートセーブ

オートセーブがいつ実行されるかは、Easy Save 3の設定で決まります。

ここのSave Eventがそうなのですが、「アプリケーションの終了、または一時停止時」しか指定できないので、今回はNone(自動で保存しない)にします。

Noneの場合、 ES3AutoSaveMgr.Instance.Save () を呼ばない限り、セーブはされません。

要するに、別のSceneへ移動する前に、 ES3AutoSaveMgr.Instance.Save () を呼び出せばいいことになります。

手動で記述すると記入漏れがありそうなので、毎回自動で呼び出されるようにしましょう。

UnityのSceneManagerにはSceneの切り替え時に呼び出されるコールバックがいくつかあるので、そこに登録しちゃいます。

    void Start () {
        SceneManager.activeSceneChanged += AutoSave;
    }

    void AutoSave (Scene thisScene, Scene nextScene) {
        ES3AutoSaveMgr.Instance.Save ();
    }

SceneManager.activeSceneChangedは、アクティブなSceneが切り替わったタイミングで呼ばれるメソッドです。要するに、「次のScene先が指定されたタイミング」で呼ばれます。

このタイミングでAutoSaveメソッドが呼ばれるように、イベントハンドラを登録します。

詳細は以下の記事が参考になります。

Sceneを切り替え後にオートロード

Sceneを切り替えた先で、以前に保存したデータがあれば、それを最速で読み込んで反映する必要があります。

これは特に難しいことはなく、Easy Save 3の設定だけでOKです。

Load EventをAwakeにすることで、Awakeが呼ばれるたびに自動でデータを読み込みます。

今さらですが、AwakeはSceneを読み込んだ際に最初に実行される関数ですが、複数あった場合の実行順序は不定です。よって、Awake関数で変数の初期化寄りを書かないようにしましょう。

Saveメソッドでファイルをコピー

Easy Save 3には複数のセーブデータを管理するスロットの概念がないため、どうやってスロットを実装するか少し悩みました。

結論を言うと、セーブする際のファイル名を分けることで、スロットを増やした扱いになります。

SaveData1.es3
SaveData2.es3
SaveData3.es3…

といったように。名前は好きなものでOKです。

つまり、手動セーブの際だけスロット番号付きの専用ファイル名にリネームしてセーブし、完了したらまたオートセーブのファイル名に戻せばいいわけです。

public void Save (int slotID) {
        ES3AutoSaveMgr.Instance.settings.path = "SaveData" + slotID.ToString() + ".es3";
        ES3AutoSaveMgr.Instance.Save ();
        ES3AutoSaveMgr.Instance.settings.path = "AutoSave.es3";
    }

ただ、これだけだと、セーブデータの保存場所と同じところに、AutoSave.es3というファイルが生成されてしまいます。
ゲーム終了時に破棄されるとはいえ、ゲーム実行中にバックアップを取られると不正な巻き戻しをされかねないので、対策します。

またこの画像かよって話ですが、Locationの項目に注目してください。
Player Prefsと指定されていますね。

要するに、オートセーブの時だけはPlayer Prefsに保存し、手動でセーブする際はファイルとして保存するように指定しています。

Scriptで指定する場合は、以下のように記述します。

// ファイルとして保存
ES3AutoSaveMgr.Instance.settings.location = ES3.Location.File;

// Player Prefsへ保存
ES3AutoSaveMgr.Instance.settings.location = ES3.Location.PlayerPrefs;

Player Prefsは、Windowsだとレジストリに記録されます。
万全とは言い難いですが、安易なコピーによるバックアップは防げます。

  1. 手動セーブの際にファイル名を変更し、ファイルとして保存する設定に変更
  2. セーブ後、ファイル名をオートセーブに戻し、保存場所をPlayer Prefsにする

以上の流れで、手動セーブが実装できます。

ゲーム終了時にオートセーブを破棄

これは簡単です。

OnApplicationQuit () という、ゲームの終了時に呼ばれるメソッドに破棄の処理を記述するだけです。

一応、改めてオートセーブの設定に直してから削除しましょう。

    void OnApplicationQuit () {
        ES3AutoSaveMgr.Instance.settings.location = ES3.Location.PlayerPrefs;
        ES3AutoSaveMgr.Instance.settings.path = AutoSaveFileName;
        ES3.DeleteFile (ES3AutoSaveMgr.Instance.settings);
    }

なお、スマホアプリの場合はOnApplicationQuitが呼ばれないので、起動時に削除する設定に直すのが良いでしょう。

もっとも、スマホアプリの場合は手動セーブ自体があまり好まれていませんので、オートセーブだけ実装しちゃえば良い気もしますが。

まとめ

以上、セーブ処理のまとめでした。

Easy Saveは非常に便利なアセットなのですが、日本語の資料が少ないのが難点ですね。少しでも役に立てば幸いです。

記事をシェアする