ゲームを保存する
CoddyのUnityジャーニー「Unityにおけるゲームアーキテクチャ」セクションの一部。レッスン 25/50。
セーブデータは、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を保存できます。チェックポイントやSaveボタンからなど、残しておく価値のあることが起きたときにセーブしてください。通常の終了時にはOnApplicationQuitが実行されますが、スマートフォンではバックグラウンドのゲームがこの処理を経ずに終了することがあります。
チャレンジ
中級探検者は矢印キーで島を歩き、Spaceキーを押すと探検者のいる場所に旗を立てます。島をファイル island.json に保存されるゲームデータにしましょう:
IslandSave.csに、[System.Serializable]を付けたクラスIslandSaveを記述し、public フィールドとしてplayerPosition(Vector3)とflags(List<Vector3>)を用意します。- Save ボタンから呼び出される
Save()は、探検者の位置と旗をJSONとして、Application.persistentDataPath内のisland.jsonに書き込みます。 Start()で、island.jsonが存在する場合は、探検者を保存された位置に戻し、保存された旗をすべて再び立てます。旗が次回の保存時にもリストに含まれるよう、PlantFlagを使います。
Play を押し、旗をいくつか立てて Save をクリックし、その後 Stop を押してから、もう一度 Play を押します。それから Check を押してください。
自分で試してみよう
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 に書き込む
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。