
【Unityゲーム開発】PlayerPrefsで大量のデータを簡単に保存する方法
お疲れ様です!日々ゲーム作りに励んでいますK.T.です。
今回のテーマはセーブデータ管理です。UnityにおけるセーブデータはPlayerPrefsを使うことが多いと思います。しかしゲームが複雑になってくると保存するデータの数も増え、PlayerPrefsの管理も複雑になりがちです。そんなときに煩雑にならずにデータを簡単に管理する方法をこの記事では紹介したいと思います。
失敗例
一般的に、PlayerPrefsでデータを保存するときは次のような感じに記述しますね。
PlayerPrefs.SetString("PlayerName", playerName);
PlayerPrefs.SetInt("PlayerLevel", playerLevel);
PlayerPrefs.SetFloat("PlayerHP", playerHP);
playerName:プレイヤーの名前
playerLevel:プレイヤーのレベル
playerHP:プレイヤーのHP
保存するデータがこれだけであればこのやり方で問題ないのですが、問題はデータの数がもっと増えた場合です。
例えばRPGのゲームを考えてみましょう。味方のキャラクターが複数います。そしてそれぞれのキャラクターごとに名前、レベル、HPやMP、装備品、所持アイテム…といった具合に保存したいデータは無数に出てきます。それを全て上の例のように記述していたらキリがありません。
//一人目のキャラクター
PlayerPrefs.SetString("Character_00_Name", character_00_Name);
PlayerPrefs.SetInt("Character_00_Level", character_00_Level);
PlayerPrefs.SetFloat("Character_00_HP", character_00_HP);
PlayerPrefs.SetFloat("Character_00_MP", character_00_MP);
PlayerPrefs.SetString("Character_00_Item_00", character_00_Item_00);
PlayerPrefs.SetString("Character_00_Item_01", character_00_Item_01);
PlayerPrefs.SetString("Character_00_Item_02", character_00_Item_02);
などなど
//二人目のキャラクター
以下同様に続く
こうなってしまっては後々大変だ!と感覚的に分かると思います。ではどうすればよいでしょうか。
解決策
クラスを丸ごと保存する
セーブデータとして保存しておきたい情報を保持しておくクラスを一つ用意し、そのクラスを丸ごとPlayerPrefsに保存します。こうすることでセーブデータの管理が飛躍的に楽になります。
具体的な方法について説明します。
下がクラスの例です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameData : MonoBehaviour
{
public string playerName;
public int playerLevel;
public float playerHP;
//セーブ
public void Save()
{
//このクラスが持つ変数をjson形式に変換
string json = JsonUtility.ToJson(this);
//PlayerPrefsに保存
PlayerPrefs.SetString("SavedData", json);
PlayerPrefs.Save();
}
//ロード
public void Load()
{
//PlayerPrefsからjson形式のデータを取得
string json = PlayerPrefs.GetString("SavedData");
//このクラスが持つ変数へ上書き
JsonUtility.FromJsonOverwrite(json, this);
}
}
このGameDataクラスを適当なオブジェクトにアタッチして次のようにプロパティを設定します。

そしてSave()を実行すると以下のようなデータがPlayerPrefsに保存されます。
{"playerName":"ドラえもん","playerLevel":12,"playerHP":129.3000030517578}
これはjsonというテキスト形式でクラスが保持していた変数が丸ごと保存されているのです。string型もint型もfloat型も全てテキスト形式になります。
(※playerHPに微小な誤差が生まれているのはUnityにおけるfloat型の仕様です。微差ですので無視しましょう)
int型やfloat型などテキスト以外の型も勝手にその型に合わせてJsonUtilityがうまいこと変換してくれるので、難しいことは考える必要が無くとても楽ですね。
このテキストへの変換のことをシリアライズといいます。シリアライズ可能な変数は全て変換し、シリアライズ不可な変数は無視されます(保存されない)。シリアライズ可能な型は他にはVector2、Vector3、Quaternionなどがあります。
逆にこのデータをロードするときはLoad()を実行します。
ゲーム中のセーブポイントでセーブするときはSave()を実行し、ゲーム開始時にロードするときはLoad()を実行すればよいというわけです。
この方法を使えばどんなにデータの数が増えてもセーブ/ロードの処理を変える必要が無いので、いくらでもデータを増やすことが可能です。
配列も保存可能
更にありがたいことに、この方法は配列およびListにも対応できます。ただしデータ型に指定するクラスはシリアライズ可能なものである必要があります。
例えば以下のような配列(string[])とList変数(List<int>)があったとします。
public string[] arraySample = new();
public List<int> listSample = new();
値は次のように設定します。

これでSave()を実行すると次のようにデータが保存されます。
{"arraySample":["こん","にち","わ"],"listSample":[3,6,13]}
配列やListの要素一つ一つをカンマで区切ってくれています。
応用
これまでのことを応用すると、RPGゲームのような大量の情報をうまく保存することが可能です。
以下のようにCharacterというクラスと、データ型がCharacterであるList変数(characterList)をGameDataクラスの中に用意します。
Characterクラスにはシリアライズ属性を付与しておきます。
public List<Character> characterList;
[System.Serializable] //シリアライズ属性を付与
public class Character
{
public string characterName;
public int level;
public float HP;
public float MP;
public List<string> itemList;
}
値は次のように設定しておきます。

これでSave()を実行すると次のようにデータが保存されます。
{"characterList":[{"characterName":"ドラえもん","level":5,"HP":30.0,"MP":20.0,"itemList":["どこでもドア","タケコプター","タイムマシン"]},{"characterName":"のび太","level":4,"HP":25.0,"MP":10.0,"itemList":["メガネ","0点の答案"]}]}
かなり見ずらいので改行と空白を入れ見やすくします。
{
"characterList":[
{
"characterName":"ドラえもん",
"level":5,
"HP":30.0,
"MP":20.0,
"itemList":[
"どこでもドア",
"タケコプター",
"タイムマシン"
]
},
{
"characterName":"のび太",
"level":4,
"HP":25.0,
"MP":10.0,
"itemList":[
"メガネ",
"0点の答案"
]
}
]
}
インスペクターで設定したのと同じ値がうまいこと保存されていますね。
このようにして大量の情報をセーブデータとしてPlayerPrefsに保存することができます。
正直これを覚えるとデータ1個1個をPlayerPrefs.SetInt()とか使って保存することは一生無い気がします…。
以上です。お役に立ちましたら幸いです。