Menu
CoddyTech

保存游戏

本课属于 Coddy Unity 学习旅程的“Unity 游戏架构”单元。第 31 课,共 64 课。

存档是一个以 JSON 格式写入的对象。把存档需要的所有内容都放进一个 [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 会读回文件内容(这三个都来自 System.IO)。游戏自己的文件夹可能是只读的,所以存档要放在 persistentDataPath 中,不能放在其他地方。在 Awake 或 Start 中构建路径:Unity 不允许在字段初始化器中使用 persistentDataPath。

存档类只保存存档需要的内容,游戏会把值复制到类中以保存,并从类中复制出来以加载。小型存档可以存入 PlayerPrefs,而不是文件:PlayerPrefs.SetString("dungeon", json) 和 PlayerPrefs.GetString("dungeon") 可以保存和读取相同的 JSON。在发生值得保存的事情时保存,例如到达检查点时,或点击“保存”按钮时:正常退出时会运行 OnApplicationQuit,但手机可能会在后台直接关闭游戏,而不运行它。

challenge icon

挑战

中等

将岛屿保存到 island.json。

  1. 在 IslandSave.cs 中,编写一个 [System.Serializable] 类 IslandSave,包含 public Vector3 playerPosition; 和 public List<Vector3> flags;。
  2. 在 Start() 中,将 path 设置为 Path.Combine(Application.persistentDataPath, "island.json")。
  3. Save() 将包含 transform.position 和 flags 的 IslandSave 保存为 JSON 文件,写入 path。
  4. 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 在线编辑器