ゲームを保存する
CoddyのUnityジャーニー「Unityのゲームアーキテクチャ」セクションの一部。レッスン 31/64。
セーブデータは、JSONとして書き込まれる1つのオブジェクトです。セーブに必要なものをすべて[System.Serializable]クラスに入れ、ゲームから値を設定し、ゲーム終了後も残る場所にそのオブジェクトのJsonUtility.ToJsonを書き込みます。このダンジョンでは、階層、ゴールド、見つけた鍵を保存します。
[System.Serializable]
public class DungeonSave
{
public int floor;
public int gold;
public List<string> keys;
}using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class Dungeon : MonoBehaviour
{
public int floor = 1;
public int gold;
public List<string> keys = new List<string>();
private string path;
void Start()
{
path = Path.Combine(Application.persistentDataPath, "dungeon.json");
if (File.Exists(path))
{
DungeonSave save = JsonUtility.FromJson<DungeonSave>(File.ReadAllText(path));
floor = save.floor;
gold = save.gold;
keys = save.keys;
}
}
public void Save()
{
DungeonSave save = new DungeonSave();
save.floor = floor;
save.gold = gold;
save.keys = keys;
File.WriteAllText(path, JsonUtility.ToJson(save));
}
}Application.persistentDataPathは、Unityが各プラットフォームでゲームに提供するフォルダーで、ゲームを終了した後も、アップデート後も保持されます。Path.Combineは、このフォルダーとファイル名をプラットフォーム固有の区切り文字で結合します。File.WriteAllText(path, text)は古いファイルを置き換えてファイルを書き込み、File.Existsはファイルがあるかどうかを知らせ、File.ReadAllTextはファイルを読み戻します(この3つはすべてSystem.IOに含まれます)。ゲーム本体のフォルダーは読み取り専用の場合があるため、セーブデータはpersistentDataPathに保存し、それ以外の場所には保存しません。パスはAwakeまたはStartで作成します。Unityでは、フィールド初期化子でpersistentDataPathを使うことはできません。
セーブ用クラスにはセーブに必要なものだけを入れ、ゲームから値をクラスにコピーして保存し、読み込むときはクラスからゲームに値をコピーします。小さなセーブデータなら、ファイルの代わりにPlayerPrefsに保存できます。PlayerPrefs.SetString("dungeon", json)とPlayerPrefs.GetString("dungeon")で、同じJSONを保持できます。残しておく価値のあることが起きたとき、チェックポイントで、または「保存」ボタンからセーブします。通常の終了時にはOnApplicationQuitが実行されますが、スマートフォンでは、バックグラウンドにあるゲームがその処理を実行せずに終了されることがあります。
チャレンジ
中級island.json に島を保存します。
IslandSave.csで、public Vector3 playerPosition;とpublic List<Vector3> flags;を持つ[System.Serializable]クラスIslandSaveを記述します。Start()で、pathをPath.Combine(Application.persistentDataPath, "island.json")に設定します。Save()は、transform.positionとflagsを含む IslandSave を JSON としてpathに書き込みます。Start()で既存のファイルを読み込みます。位置を復元し、各フラグに対してPlantFlagを呼び出します。
「プレイ」を押し、旗を立てて「保存」を押し、「停止」してから「プレイ」を押すと、旗が戻っています。その後、「確認」を押します。
自分で試してみよう
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.InputSystem;
public class Explorer : MonoBehaviour
{
public GameObject flagPrefab;
public float speed = 4f;
private List<Vector3> flags = new List<Vector3>();
private InputAction moveAction;
void Start()
{
moveAction = InputSystem.actions.FindAction("Move");
// ここで island.json を読み込む
}
void Update()
{
Vector2 input = moveAction.ReadValue<Vector2>();
transform.Translate(new Vector3(input.x, 0f, input.y) * speed * Time.deltaTime);
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
PlantFlag(transform.position);
}
}
void PlantFlag(Vector3 position)
{
flags.Add(position);
Instantiate(flagPrefab, new Vector3(position.x, 0.5f, position.z), Quaternion.identity);
}
// Save ボタンから呼び出される
public void Save()
{
// プレイヤーの位置と旗の情報を island.json に書き込む
}
}
Unityのゲームアーキテクチャのすべてのレッスン
自分で練習してみよう: Unityプレイグラウンド