PlayerPrefs を使用してデータを読み書きする
検証環境
- Windows
-
- Windows 11
- Unity エディター
-
- 2021.3.3f1
- 入力システムパッケージ
-
- 1.3.0
この Tips の前提設定
この Tips の説明の前提として以下の設定を事前に行っています。
はじめに
ゲームを遊んだことがあるならばほとんどの人は経験したことがあると思いますが、 ゲームを中断して続きから遊ぶためにプレイデータを保存したり読み込んだりしたことがあると思います。
ゲームを作るうえではこのセーブやロードの機能、オプションの設定などは大抵の場合作る必要があります。
ここではデータの保存や読み込みの方法が最も簡単な PlayerPrefs
を使用して実現してみます。
UI の配置
ここでは入力項目とボタンを配置してボタンをクリックすることによって入力内容を保存したり読み込んだりしてみます。 とりあえず最低でも2つのボタンと1つの入力フィールドがあればいいので自由に配置してみてください。
UI 処理の下準備
まずは保存ボタンの実装を行います。ボタンイベントの作り方は以下の Tips で紹介していますので関係のある部分だけ載せておきます。
スクリプト名は ButtonEvent
としておきます。
スクリプトを以下のようにします。
using UnityEngine;
using UnityEngine.UI;
public class ButtonEvent : MonoBehaviour
{
<summary>名前入力フィールド。</summary>
[SerializeField] InputField InputFieldName;
<summary>HP 入力フィールド。</summary>
[SerializeField] InputField InputFieldHP;
<summary>攻撃力入力フィールド。</summary>
[SerializeField] InputField InputFieldAttack;
<summary>お金入力フィールド。</summary>
[SerializeField] InputField InputFieldMoney;
<summary>
保存ボタンをクリックしたときの処理。
</summary>
public void OnClickSave()
{
}
<summary>
読み込みボタンをクリックしたときの処理。
</summary>
public void OnClickLoad()
{
}
}
入力した値を取得したりセットする必要があるのでフィールドとして用意しておきます。 またそれぞれのボタンをクリックしたときに呼ばれるメソッドを入れておきます。
作成したスクリプトは EventSystem
にアタッチし、各入力フィールドをセットします。
アタッチするオブジェクトはどれでも構いません。
ボタンをクリックしたときに各メソッドが呼ばれるようにセットします。
値を保存する
保存ボタンをクリックしたときに OnClickSave
メソッドが呼ばれるようにしたので、保存ボタンの処理を以下のようにします。
<summary>
保存ボタンをクリックしたときの処理。
</summary>
public void OnClickSave()
{
// 各値を指定したキーに保存します
PlayerPrefs.SetString("Name", InputFieldName.text);
PlayerPrefs.SetString("HP", InputFieldHP.text);
PlayerPrefs.SetString("Attack", InputFieldAttack.text);
PlayerPrefs.SetString("Money", InputFieldMoney.text);
// 設定したデータを確定して保存します
PlayerPrefs.Save();
}
PlayerPrefs.SetString
メソッドにはキーと保存する値をセットします。キーは保存したデータを読み込む際に指定することになります。
ここでは SetString
メソッドを呼んでいますが、これは保存する値が文字列であるためです。
int
や float
など別の型の値を保存する場合は SetInt
や SetFloat
などのメソッドがあるので型に合わせて呼んでください。
最後に PlayerPrefs.Save
メソッドを呼んで保存を確定します。実はこのメソッドを呼ばなくても保存されてたりしますが呼んでおいた方が無難です。
ただこの時点でゲームを実行しても値を保存するだけですので、きちんと保存されたかどうかは分かりません。 一応保存された場所を確認すればデータは見れますが…。
保存した値を取得する
次に保存したデータを読み込む処理を実装します。ボタンの処理は保存処理と同じなのでここでは読み込み処理のコードのみ説明します。
<summary>
読み込みボタンをクリックしたときの処理。
</summary>
public void OnClickLoad()
{
// 指定したキーから値を読み込みます
InputFieldName.text = PlayerPrefs.GetString("Name");
InputFieldHP.text = PlayerPrefs.GetString("HP");
InputFieldAttack.text = PlayerPrefs.GetString("Attack");
InputFieldMoney.text = PlayerPrefs.GetString("Money");
}
保存したデータを読み込むには PlayerPrefs.GetString
メソッドを使います。
引数に保存で指定したキーを指定することによりそのキーで保存した値を取得することができます。値はメソッドの戻り値で取得します。
これも保存の時と同じように文字列で保存しているので GetString
メソッドを呼んでいます。
int
や float
で保存しているなら GetInt
, GetFloat
メソッドを呼んでください。
取得した値は入力フィールドに反映させています。
これでデータの保存と読み込みが実現できました。難しい要素はないと思います。
動かしてみる
実行して入力、保存、読み込みを行ってください。保存したデータは永続化されるのでゲームを終了して再実行しても読み込めるはずです。 以下のパターンを確認できれば問題ないと思います。
- 値を入力する
- 保存ボタンをクリックする
- 入力した値を変更する
- 読み込みボタンをクリックして保存した値に戻るか確認
- ゲームを終了する
- ゲームを再実行して読み込みボタンをクリックし、保存した値を呼び出せるか確認
- 別な値を入力して保存し、更新した値で同じように読み込めるか確認
まとまったデータを保存・読み込みする
ここまでのサンプルでは保存するパラメータは数個程度でしたが、 実際にゲームを作っていくとセーブすべきパラメータはどんどん増えていき、1個1個読み書きするとコードが膨大になってしまいます。 ですので実際に保存するデータを作る場合は保存するデータを1つにまとめてから一気に書き込むのがセオリーとなります。
まずはセーブデータの構造をクラスとして作成します。 一度にデータの読み書きができるようになるという目的もありますが、データをクラス化することによってコードから各データにアクセスしやすくなるメリットもあります。
以下はセーブデータのクラスの例です。他のコードから参照できるならばどこに記述しても構いません。 このクラス構造はサンプルなので実際のゲームではゲームにあわせて作る必要があります。
<summary>セーブデータクラス。</summary>
public class SaveData
{
public List<Character> Characters;
public long Money;
}
<summary>1キャラクターの情報。</summary>
[Serializable]
public class Character
{
public string Name;
public int HP;
public int Attack;
}
今回使用する Unity 標準の機能では配列や List
で独自クラスを複数持てるように定義している場合、
System.Serializable
属性を付与しないと正しく変換されないので Character
クラスに Serializable
属性を設定しています。
クラスを作成したら保存するデータを作成します。 もちろんこのクラスのインスタンスをゲーム全体で持ちまわしてもOKです。これはゲームの作り方に依存します。
// 保存するデータ作成
var saveData = new SaveData
{
Money = 10000,
Characters = new List<Character>
{
new Character { Name= "レイシア", HP = 50, Attack = 40, },
new Character { Name= "アイリ", HP = 56, Attack = 27, },
new Character { Name= "ニール", HP = 72, Attack = 36, },
new Character { Name= "トリー", HP = 61, Attack = 30, },
},
};
クラスのインスタンスにデータをセットしたら JsonUtility.ToJson
メソッドで JSON 文字列に変換(シリアライズ)します。
オブジェクトの値がデータ構造を持った JSON という単一の「文字列」になるため、PlayerPrefs.SetString
メソッドを1回呼ぶだけで全てのデータを書き込むことができます。
// オブジェクトを JSON 文字列に変換します
var saveJson = JsonUtility.ToJson(saveData);
// データを保存します
PlayerPrefs.SetString("SaveData", saveJson);
PlayerPrefs.Save();
ただし全てのデータがひとつにまとまっているので、一部だけ保存する、一部だけ読み込む、といった処理はできません。 なのでセーブデータ1個に対して1オブジェクトとしてまとめるなどまとめやすい単位にするのが好ましいです。
保存したデータを読み込むときは保存するときの逆の処理をやればOKです。
読み込むデータは単一の文字列ですが中身は JSON 形式のデータ構造になっているので JsonUtility.FromJson
メソッドでクラスインスタンスに変換(デシリアライズ)することが出来ます。
ただし、何のクラスに変換するかは文字列に情報を持ってないので FromJson
メソッドを呼ぶときに型を指定する必要があります。
// データを読み込みます
var loadJson = PlayerPrefs.GetString("SaveData");
// JSON 文字列からオブジェクトにデシリアライズします
var newData = JsonUtility.FromJson<SaveData>(loadJson);
動かしてみると正しく保存され、読み込めていることを確認できると思います。
ちなみに今回 Unity 標準の JsonUtility
クラスを使用していますが、あまり機能は充実していないため複雑なデータ構成のクラスは変換できない場合があります。
その場合は他のライブラリなど使用することをお勧めします。
データが保存される場所
PlayerPrefs
クラスを使用してデータを保存した時に保存される場所は、実行しているプラットフォームによって決まっています。
実際にどこに保存されるかは以下の公式ページなどを参照してください。
Windows など一部の環境ではファイルに保存されるのではなくレジストリなどシステムの設定に保存されるものもあります。
ファイルでないため、例えばセーブデータが非常に大きい場合や他の環境とセーブデータを同期したい場合などには PlayerPrefs
での保存は不向きです。
逆に保存するデータがオプションレベルのサイズであったり、実行している環境のみで完結するスタンドアロン型のゲームであれば PlayerPrefs
でデータを保存するのはありだと思います。
また、保存される場所のパスは「CompanyName」や「ProductName」の設定が影響する場合があります。 ゲームを公開する場合は前もってこれらの値はきちんと決めておき、一度ゲームを公開したら変えないようにする必要があります。
データの暗号化について
保存されるデータは自動的に暗号化されることはありません。 プレイヤーの能力などそのまま保存してしまうとユーザーは自由に値を書き換えてしまいゲームに影響がでてしまいます。
暗号化する必要があるのか、必要ならどのレベルの強度が必要なのか、暗号化のキーの扱いはどうするのか、 など考慮すべき点はゲームによって異なります。
ここでは暗号化の方法については説明しませんが、ネットで調べればそれなりにメジャーな言語、ツールなのでいろいろ方法があると思います。 とりあえずファイルを開かれても保存されている内容の意味が分からないようにし、下手にデータをいじったらセーブデータが使えなくなるようにしてしまえばそれなりに不正対策の効果はあると思います。