Unity3D RPG Core | 33 保存数据
这节介绍使用 Unity 自带的 PlayerPrefs 机制保存游戏数据。
1. 实现保存功能
我们新建一个 C# 脚本,命名为 SaveManager,它也是一个单例类。
如代码清单 1 所示,我们编写了基础的 Save() 和 Load() 函数,它针对于所有类型的类。我们通过 JsonUtility.ToJson 来将类转化成 json 字符串;JsonUtility.FromJsonOverwrite 将 json 字符串中的内容覆盖到现有类上。PlayerPrefs.SetString 和 PlayerPrefs.GetString 可以写入字符串和获取字符串,即可以用于我们生成的 json 文本。PlayerPrefs.Save 用于保存数据,实现可持久存储。
视频中是把键的名字用对象名称指定。本地实验下来发现,预制体实例化出来的对象名称会额外加上 "Clone" 字样,导致键不匹配。
这边将键名称写死。
- public class SaveManager : Singleton<SaveManager>
- {
- // Update is called once per frame
- void Update()
- {
- if (Input.GetKeyDown(KeyCode.S))
- {
- SavePlayerData();
- Debug.Log("SavePlayerData");
- }
- else if (Input.GetKeyDown(KeyCode.L))
- {
- LoadPlayerData();
- Debug.Log("LoadPlayerData");
- }
- }
- protected override void Awake()
- {
- base.Awake();
- DontDestroyOnLoad(this);
- }
- public void SavePlayerData()
- {
- PlayerDataScriptableObject playerData = GameManager.GetInstance().m_player.m_characterData.m_playerData;
- Save(playerData, "PlayerData");
- AttackDataScriptableObject attackData = GameManager.GetInstance().m_player.m_attackData.m_attackData;
- Save(attackData, "PlayerAttackData");
- }
- public void LoadPlayerData()
- {
- PlayerDataScriptableObject playerData = GameManager.GetInstance().m_player.m_characterData.m_playerData;
- Load(playerData, "PlayerData");
- AttackDataScriptableObject attackData = GameManager.GetInstance().m_player.m_attackData.m_attackData;
- Load(attackData, "PlayerAttackData");
- }
- void Save(object obj, string key)
- {
- string json = JsonUtility.ToJson(obj);
- PlayerPrefs.SetString(key, json);
- PlayerPrefs.Save();
- }
- void Load(object obj, string key)
- {
- if (PlayerPrefs.HasKey(key))
- {
- string json = PlayerPrefs.GetString(key);
- JsonUtility.FromJsonOverwrite(json, obj);
- }
- }
- }
代码清单 1 中的 SavePlayerData() 和 LoadPlayerData() 用于存储和加载角色信息数据,包含人物数值和攻击数值。需要注意的是,作用的类需要是 ScriptableObject。
需要注意读写需要作用在 ScriptableObject 类上。
发现之前跟着教程的做法,针对 ScriptableObject 的封装有点冗余。
1.1 保存测试
在代码清单 1 中,我们在 Update() 函数中编写测试代码:按下 S 键保存,按下 L 键加载。我们可以在进入游戏后保存人物数据。然后打怪让人物信息发生变化后,再加载之前保存的数据,看界面上 UI 是否按预期显示。
在 Windows 系统上,PlayerPrefs 相关内容存储在注册表上。如图 1 所示,我们可以找到地方,具体查看一下键值。

2. 完善场景切换逻辑
切换不同场景时,人物是通过预制体实例化出来的,所以需要同步传送前的人物数据信息。
如代码清单 2 所示,我们在场景加载之前保存当前人物角色数据,到了新场景后再加载之前保存的数据,达到数据同步的效果。
特别需要注意时序的问题,需要是这样的时序:保存人物数据;人物对象 Disable,取消 GameManager 的注册;生成新人物对象,在 GameManager 上注册;加载人物数据。即加载的数据需要作用在最新的人物对象上。
- public class SceneController : Singleton<SceneController>
- {
- IEnumerator TransferDifferentScene(string sceneName, string destPortalName)
- {
- SaveManager.GetInstance().SavePlayerData();
- AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
- // Wait until the asynchronous scene fully loads
- while (!asyncLoad.isDone)
- {
- yield return null;
- }
- PortalController destPortal = GetDestinationPortal(destPortalName);
- Instantiate(m_playerPrefab,
- destPortal.m_point.position,
- destPortal.m_point.rotation);
- while (GameManager.GetInstance().m_player == null)
- {
- yield return null;
- }
- SaveManager.GetInstance().LoadPlayerData();
- }
- }
上一篇文章中在地下城场景中还没有人物信息 UI。添加的方式也很简单,将原先场景中的人物信息 UI 存为预制体,再拖拽到新场景中即可,相关逻辑是通用的。
新场景添加完人物信息 UI 之后,测试的话就很方便。只需要看传送到新场景后人物信息是否和传送前一致。