Unityでゲームデータのセーブ・ロードを行う方法

今回はUnityでゲームのデータを保存してみたいと思います。

ゲームのプレイデータを保存出来るようにし、同じ状態で再開出来るというのは今や必須の機能ですね。

昔のゲームみたいにパスワードを入力していく機能を実装するのもある意味面白いとは思いますが・・・・(^_^;)

Unityでゲームデータを扱うのは難しくありません。

スポンサーリンク

Unityでゲームデータを扱う方法

データを保存

↑のようにPlayerPrefsを使うと第1引数で指定した名前のキーを作成し、そこにデータを保存する事が出来ます。

第1引数には保存するデータの名前を指定します。

関数名は保存するデータの形式を指定します。

データの読み出し

値を取り出す時は

とするだけです。

取得したデータをキャラクターのステータススクリプトのHPやMP等のデータに代入し使用します。

データの削除

データを削除する時は

↑のように特定のデータを削除したり、全部のデータを削除する事も出来ます。

指定したデータが存在するかどうか

データが存在するかどうかは↑のようにHasKeyの引数にデータの名前を指定します。

データが保存されるタイミング

今まで見て来たデータの更新はOnApplicationQuitイベントの中で行われるようです。
OnApplicationQuit関数はアプリケーションが終了する直前に実行されるので途中でゲームが止まってしまった場合はデータの保存がされません。

こういったことがないように

↑のように呼び出すと強制でデータ保存がされるようになります。

ですが、この処理は時間がかかるようでゲームプレイ中にこの処理をさせるのはよくないようです。

詳しくは

https://docs.unity3d.com/ja/current/ScriptReference/PlayerPrefs.html

Unityのスクリプトリファレンスに記載があるので参照してください。

ゲームデータを作り実際に操作してみる

JSONデータについて

それではこれらの機能を使って実際にデータを保存したり読み出したりしてみたいと思います。

単純なデータの保存はさきほど見て来た通りにシンプルに記述すれば出来ますので、

今回は保存したいデータをJSON形式で保存したりそのデータを読み出して
再度ゲームデータに戻すという事をしてみたいと思います。

JSON形式はデータの保存方法の1つで

{“hp”:10,”mp”:5}

という感じでデータを扱います。

なぜJSON形式でデータを保存するかと言うとオブジェクトのプロパティ値を1つのキーで保存する事が出来る為です。

例えばhp、mpを保存したい場合

と複数の記述が必要ですが、JSON形式のデータはString型なので一括で保存出来ます。

例えばdata変数に

{“hp”:10,”mp”:5}

のようなStringのデータが入っているとすれば

とすればいいので1つ1つのデータを個別に処理する必要がなくなります。

JSON形式でデータを保存と読み出しをする為には保存するデータがSerializable属性で宣言されているオブジェクトに限ります。

Serializable属性はC#だと明示的にしないといけませんがJavaScriptの場合はデフォルトでなっているようです。

また、保存出来るデータはpublic変数のみで、static、privateで宣言された変数はデータ保存されません。

それを含めて確認してみます。

JSON形式のデータを作成する場合は

↑のように記述すればJSONデータを扱えます。
3番目はうまく扱えなかったので今回は見送ります・・・・(^_^;)

ゲームデータを扱うスクリプトSavaDataの作成

それではゲームデータを扱うスクリプトSaveDataを作成しましょう。

static、privateの変数が保存されないか確認する為に宣言しておきます。

LoadData関数は『PlayerData』というデータがあればそのデータから自身のプロパティ値を更新しそのデータを返す関数です。

データがなければ空文字を返します。

DeleteData関数は全データを問答無用で削除します。

GetNormalData関数はJSON形式ではなく自分で記述したテキスト形式のデータを返します。

GetJsonData関数は自身のプロパティのJSON形式のデータを返します。

このSaveDataスクリプトで宣言されているhp、mp、power、flagをJSON形式のデータに変換しセーブするデータとします。

実際はpublicで宣言されたhp、flagしかデータ保存がされないことになります。

ボタンを押した時に呼び出すスクリプトOperationTextの作成

次はボタンを押した時に呼び出すスクリプトOperationTextを作成していきます。

このスクリプトはボタンが押された時にさきほどのSaveDataスクリプトを呼び出したり、データをUIテキストに表示する為のものです。

ほとんどがSaveDataスクリプトの関数の呼び出しなので問題はないと思います。

SaveData関数(SaveDataスクリプトではありません)で取得したJSON形式データを保存しています。

データ書き換えスクリプトOperationInputFieldの作成

次にデータを書き換えて確認する為のデータ書き換えスクリプトOperationInputFieldを作成します。

やっている事はSaveDataスクリプトに最初のデータを保存したり、入力フィールドに値が入力されたらその値をSaveDataスクリプトの変数に上書きする処理です。

(ただのサンプルの為、入力フィールドに数値以外の値が入力された時の処理等は行っていません)

ゲームデータの確認サンプルUIの作成

ではこれらのスクリプトを実行する為のサンプルを作成していきます。

実際にデータを書き込めるかというサンプルなので詳細な作り方の説明は割愛させて頂きます。

データセーブのサンプルUI

↑のような構成で『データ表示部』『データ入力部』『データ処理ボタン部』とわけて作成します。

データ表示部はデータ処理ボタンが押された時にそのデータを表示する部分です。

データ入力部はflagデータ以外のデータを入力してOperationInputFieldスクリプトの関数を呼び出しています。

データ処理ボタン部はボタンを押すとOperationTextスクリプトの関数を呼び出しています。

データセーブのサンプルUIの実際の画面

構成は↑のようになります。

CanvasにSaveDataスクリプト、OperationTextスクリプトを設定します。

OperationTextのインスペクタでDataTextを設定します(DataTextがデータ表示部のUIテキストです)。

ShowParameterボタンはノーマルのデータをデータ表示部に表示するボタンでButtonのOnClickにCanvasゲームオブジェクトに設定したOperationTextのShowParameter関数を呼び出すように設定します。

ShowボタンはJSON形式のデータを出力するボタンでOperationTextのShowJsonData関数を設定します。

Loadボタンは保存したデータを読み出してJSON形式のデータを出力するボタンでOperationTextのLoad関数を設定します。

SaveボタンはJSON形式のデータを保存するボタンでOperationTextのSave関数を設定します。

Deleteボタンは保存したデータを削除するボタンでOperationTextのDelete関数を設定します。

ボタンを押した時のイベントの実行に関しては

UnityのUIのクリックやドラッグのイベントを受け取る
UnityのUIのクリックやドラッグのイベントをEvent Triggerコンポーネントを取りつけて受け取れるようにします

を参照してください。

InputDataPanelにOperationInputFieldスクリプトを設定します。

HPFieldはhpの値を入力する入力フィールドでOnEndEditイベントの所にInputDataPanelをドラッグ&ドロップしOperationInputFieldのSetHp関数を設定します。

MPFieldはmpの値を入力するフィールドでHPFieldと同じようにSetMp関数を設定します。

PowerFieldはpowerの値を入力するフィールドでSetPower関数を設定します。

これらのUIはさきほどのスクリプトの関数を呼び出すだけで今回の記事とはそれほど関係ありません。

なのでご自由にUIを作成しスクリプトを呼び出して確認してみてください。

UIの細かい説明を書いていると目的の内容と違う内容の記述が多くなってしまうので・・・(^_^;)

これで機能が完成したので、Unityの実行ボタンを押してデータを確認してみましょう。

JSON形式でのデータセーブのサンプル動画

ボタンが赤くなっている順番にボタンを押していってます。

hpが10の時にデータを保存し、hpの入力フィールドに30を入力してデータ表示ボタンを押すとhpは30で出力されますが、データ読み出しボタンを押すとhpが10となります。

保存したデータが反映されている事がわかりますね!

またデータを削除してから読みだすとデータがない為何も表示されません。

今回のサンプルスクリプトは作り込んではいない為入力フィールドの値とSaveDataに保存されている変数のデータは必ずしも一致しません。

それは入力フィールドを変更したあとにデータを読み込むボタンを押して変数データを上書きしても入力フィールドの書き換えをしていない為です。

そこら辺はまぁ・・・サンプルなので許してください(^_^;)

長ったらしく書いていますが、JSON形式でまとめて保存しなくても

と個別で保存するのでもいいと思います。

privateやstaticで宣言されたデータは保存されないので間違ってprivateで宣言していてデータを読み出したけど前のデータのままだった
なんて事も起こりえます。

またオブジェクトにJSONデータを反映する時に保存されているデータ以外は上書きされずそのままになるのかどうかもちょっとわかりません。

変数名とキー名が合っているものだけを上書きしているんですかね?

長い間使ったわけではないのでそこら辺はちょっとわからないです(^_^;)

ゲームデータをゲームフォルダにファイルとして保存する

PlayerPrefsを使うとゲームデータの保存は出来ますが、ゲームフォルダには保存されません。

そこでゲームデータをゲームフォルダ内に指定したファイル名で保存してみたいと思います。

UnityWebplayerやWebGL形式のゲームではファイル操作が出来ないと思うので対象外です(PlayerPrefsを使ってください)。

この処理に関しては

Do you want to know how to keep your data between scenes? How about between executions of your game? During this session we will learn how to do both. We will l...

を参考にさせて頂きました。

ファイル操作のサンプルUIを作成する

まずはファイル操作をする時に使うUIを作成していきます。

入力フィールドとボタンを作成

↑のようにCanvasを作り、子要素にSaveボタン(UI→Button)、Loadボタン(UI→Button)、Dataフィールド(UI→InputField)を作成します。

入力フィールドとセーブ・ロードボタンの画像

↑がUIの位置を調整した画像です。

LineTypeを変更する

Data(InputField)を選択して、インスペクタのInputFieldのLine TypeをMulti Line Newlineに変更します。

これは入力フィールド内でEnterキーを押して改行した場合に次の行に移りそのまま入力が出来るようにする為です。

データをファイルに保存したり読み出したりするSaveLoadFileスクリプトの作成

それではデータをファイルに保存したり読みだしたりするスクリプトSaveLoadFileを作成します。

ファイルの読み書きをするにはC#のファイル読み書きオブジェクトを使います(Unityのスクリプトファイルには載っていない)。

C#を使ったことがある方は問題はなさそうですね(わたくしにとっては辛い・・・)。

行っていることはSave関数が呼ばれた時にApplication.dataPathでゲームフォルダを取得しファイル名を指定してファイルの作成、

保存するデータをBinaryFormatterでシリアライズしてファイルに保存しています。

保存するデータはデータ用クラスを作成し、public変数で宣言したデータを格納するようになっています。

try-catch-finallyは例外処理でファイルを作成する時に例外が発生した時の処理を記述しています(最後のファイル閉じの例外処理してないかも・・・)。

ここら辺はJavaやC#の本を見た方がいいかも・・・(^_^;)

今回作成したSave関数では常に新しいファイルを作成する仕様になっていますが、ファイルが存在する時は上書きにするといったように書き換えるといいと思います。

Load関数は保存したデータを読み出してデシリアライズし入力フィールドDataに表示しているだけです。

これでスクリプトが出来たので、Canvasにこのスクリプトを設定してください。

SaveLoadFileスクリプトのインスペクタのinputFieldにはData(InputField)を設定してください。

SaveボタンのOnClickイベントにはCanvasのSaveLoadFileスクリプトのSave関数を設定し、
LoadボタンのOnClickイベントにはCanvasのSaveLoadFileスクリプトのLoad関数を設定します。

イベントに関しては

UnityのUIのクリックやドラッグのイベントを受け取る
UnityのUIのクリックやドラッグのイベントをEvent Triggerコンポーネントを取りつけて受け取れるようにします

を参照してください。

ファイルが出力されるか確認する

それではUnityの実行をして確認してみましょう。

ファイルの保存と読み出しのサンプル

↑のように入力フィールドに文字を書いた後Saveボタンを押しファイルに書き込んで入力フィールド内を変更、
そしてLoadボタンを押すと保存したファイルを読み込んで表示する為さきほどの文字列が再び表示されます。

UnityEditorにファイルが作成された

UnityEditorを実行中にApplication.dataPath以下にファイルを作成するとAssets以下にファイルが作成されるので、確認してみてください。

スタンドアロン形式でビルドし、名前をaとするとa.exeとa_Dataというフォルダが同じ階層に出来ます。

ゲームを実行してファイルを作成すると

ファイルが作成された

↑のようにゲームフォルダ(a_Data)以下にゲームデータファイルが作成されていることが確認出来ました。

これでゲームデータをファイルに出力出来ることがわかりました。

C#でスクリプトを書き例外処理を簡単にする

JavaScriptでスクリプトを書くと例外処理の記述が面倒くさいですが、C#のusingを使うと例外処理の記述をしなくても勝手にやってくれるっぽい?

というわけでおまけにC#での記述をしてみました。

わたくしはC#は使ったことがないので参考程度でお願いします。

ファイル名はFileSaveLoadCという名前にしました(名前が似通っててわかりづらい・・・・)。

ほとんどJavaScriptの処理と同じですが、ファイルの作成・読み込みの時にusingを使っています。
try-catch-finallyがないので楽ですね!

イベントを受け取る時はSave、Loadがpublicでないとダメみたいなのでpublicにしました。

また、C#でシリアライズ・デシリアライズする為には[Serializable]という記述が必要なので追記しました。

JavaScriptではデフォルトでなっているので記述しなくて大丈夫です。

SaveLoadFileスクリプトを取り外し、FileSaveLoadCスクリプトをCanvasに取りつけ、Save・Loadボタンを押した時に実行する関数も変更してください。

確認するとJavaScriptと同じように処理が出来るのを確認出来ると思います。

外部ファイルにデータを保存するのはゲームデータの容量が大きくなった時だと思います。

小規模のゲームならPlayerPrefsを使うだけでいいような気がします。
(PlayerPrefsでデータを保存出来るのは10MBぐらい?)

ファイルを操作するのってなんか怖いですもんね(^_^;)

この記事はずいぶん前に書いた(と言っても2カ月ぐらい前)内容で本日あらためて確認の為に記事を読み直しましたが、なんだかサンプルがわかり辛いですね・・・。

もっと簡単にセーブ、ロードが確認出来るサンプルにすればよかったと思いました。

思ったなら簡単なサンプルを作成せい!(-_-)/~~~ピシー!ピシー!

サンプルの部分を考えなければデータのセーブとロードの部分が楽に出来るというのが解っていただけるかも?