保存进度:PlayerPrefs、JSON,以及各自适用的场景
怎么保存玩家的进度?
零星的设置用 PlayerPrefs,真正属于游戏状态的东西则用 Application.persistentDataPath 下的一个 JSON 文件来存。在 OnApplicationPause 和 OnApplicationQuit 里都要写文件——在手机上,Quit 往往根本不会被调用。
Unity 给你两种保存方式,它们不是竞争关系:PlayerPrefs 管一小撮设置,JSON 文件管游戏状态。用 PlayerPrefs 存游戏状态,这种错误往往要等项目走到离发售只剩一半时才暴露。
PlayerPrefs —— 只用来存设置
PlayerPrefs.SetFloat("music", 0.7f);
PlayerPrefs.SetInt("language", 1);
PlayerPrefs.Save();
float music = PlayerPrefs.GetFloat("music", 1f); // 1f if never set它只能存三种类型:int、float 和 string。其他东西都得你自己压成这三种之一再塞进去。
它在磁盘上就是明文 —— Windows 上是注册表,macOS 上是 .plist,Android 上是 XML 文件。玩家想改自己的分数,打开文本编辑器就能改。
既没办法列出里面存了什么,也没办法做版本管理。存档格式一旦长大,就会超出 PlayerPrefs 的承受范围,而且没有任何迁移的路。
JSON —— 保存真正的游戏状态
using System.IO;
using UnityEngine;
[System.Serializable]
public class SaveData
{
public int version = 1; // pays for itself the first time you change the format
public int level;
public float health;
public string[] unlocked;
}
public static class SaveSystem
{
// persistentDataPath is the only folder that survives an app update on a
// phone, and the only one you are allowed to write to on iOS.
static string Path => System.IO.Path.Combine(Application.persistentDataPath, "save.json");
public static void Save(SaveData data)
{
// Write to a temporary file, then swap. A crash mid-write then loses
// the new save instead of destroying the old one.
string tmp = Path + ".tmp";
File.WriteAllText(tmp, JsonUtility.ToJson(data, true));
File.Copy(tmp, Path, true);
File.Delete(tmp);
}
public static SaveData Load()
{
if (!File.Exists(Path)) return new SaveData();
return JsonUtility.FromJson<SaveData>(File.ReadAllText(Path));
}
}JsonUtility 能序列化和不能序列化什么
它保存 public 字段和标了 [SerializeField] 的 private 字段,带 { get; set; } 的 property 则整个被忽略 —— 这一点常常让人措手不及。
它序列化不了 Dictionary。存成两个平行数组,或者一个由小的 [System.Serializable] 类组成的列表。
类本身也要标 [System.Serializable],而且不能继承 MonoBehaviour。存档数据是数据,不是组件。
在正确的时机保存
// Android and iOS kill a backgrounded app without ever calling
// OnApplicationQuit. Pause is the last callback you are guaranteed.
void OnApplicationPause(bool paused)
{
if (paused) SaveSystem.Save(current);
}
void OnApplicationQuit()
{
SaveSystem.Save(current);
}做出这个的那一课
保存进度
这篇指南单独成篇,是一份可以直接照着做的配方。在课程里,同样的东西会作为贯穿全部五个章的那个项目的一部分来搭建 —— 导出到手机,第 04 课。