Unity3D激活场景保存问题:如何保存加载后场景的修改并复现
我懂你现在的困境——你的代码只能记住当前是哪个场景,完全没法还原你在场景里做的那些修改,比如新生成的敌人、摆放的道具对吧?别着急,我们可以通过序列化游戏对象的关键状态数据来解决这个问题,下面是一套完整的实现方案:
解决方案步骤
1. 创建可序列化的保存数据类
首先我们需要定义专门的类来存储每个需要保存对象的关键信息,这个类要标记为[Serializable],这样Unity才能把它转换成可存储的JSON或二进制数据。
using UnityEngine; using System.Collections.Generic; [System.Serializable] public class SavedObjectData { // 预制体唯一标识(用来确定要实例化哪个预制体) public string prefabUniqueId; // 对象的位置信息 public Vector3 position; // 对象的旋转信息 public Quaternion rotation; // 可扩展:添加你需要保存的其他属性,比如生命值、物品数量等 // public int currentHealth; } [System.Serializable] public class GameSaveData { // 场景索引(保留你原来的逻辑) public int sceneIndex; // 所有需要保存的对象数据列表 public List<SavedObjectData> savedObjects; }
2. 修改SaveGame方法:收集并保存场景内的修改
现在我们要遍历场景中所有需要保存的对象(建议给这些对象加个Saveable标签方便筛选),把它们的状态数据收集到GameSaveData中,再转成JSON字符串存储起来(这里用PlayerPrefs,大数据量建议用本地文件)。
using UnityEngine.SceneManagement; using System.Collections.Generic; public void SaveGame() { GameSaveData saveData = new GameSaveData(); saveData.sceneIndex = SceneManager.GetActiveScene().buildIndex; saveData.savedObjects = new List<SavedObjectData>(); // 筛选场景中所有标记为"Saveable"的对象 GameObject[] saveableObjects = GameObject.FindGameObjectsWithTag("Saveable"); foreach (GameObject obj in saveableObjects) { SavedObjectData objData = new SavedObjectData(); // 这里假设预制体的名字是唯一标识,也可以给预制体加自定义组件存Guid更可靠 objData.prefabUniqueId = obj.name; objData.position = obj.transform.position; objData.rotation = obj.transform.rotation; // 示例:如果对象有自定义状态组件,读取并保存 // PlayerStatus status = obj.GetComponent<PlayerStatus>(); // if (status != null) // { // objData.currentHealth = status.Health; // } saveData.savedObjects.Add(objData); } // 转成JSON字符串并保存 string saveJson = JsonUtility.ToJson(saveData); PlayerPrefs.SetString("FullGameSave", saveJson); PlayerPrefs.Save(); Debug.Log($"游戏保存完成!共记录了 {saveData.savedObjects.Count} 个对象的状态"); }
3. 修改LoadGame方法:读取数据并恢复场景状态
加载时先读取保存的JSON数据,转成GameSaveData,然后加载对应场景,必须等场景加载完成后再实例化对象并恢复状态(用场景加载回调处理异步逻辑)。
// 临时存储保存数据的类成员变量 private GameSaveData _currentLoadedSaveData; public void LoadGame() { if (!PlayerPrefs.HasKey("FullGameSave")) { Debug.LogWarning("未找到任何保存数据!"); return; } // 读取并解析JSON数据 string saveJson = PlayerPrefs.GetString("FullGameSave"); _currentLoadedSaveData = JsonUtility.FromJson<GameSaveData>(saveJson); // 加载目标场景,并注册加载完成回调 SceneManager.LoadScene(_currentLoadedSaveData.sceneIndex); SceneManager.sceneLoaded += OnSaveSceneLoaded; } private void OnSaveSceneLoaded(Scene scene, LoadSceneMode mode) { // 取消回调注册,避免重复触发 SceneManager.sceneLoaded -= OnSaveSceneLoaded; if (_currentLoadedSaveData == null) return; foreach (SavedObjectData objData in _currentLoadedSaveData.savedObjects) { // 从Resources文件夹加载预制体(大项目建议用Addressable或预制体字典) GameObject targetPrefab = Resources.Load<GameObject>(objData.prefabUniqueId); if (targetPrefab != null) { // 实例化对象并恢复状态 GameObject spawnedObj = Instantiate(targetPrefab, objData.position, objData.rotation); // 示例:恢复自定义状态 // PlayerStatus status = spawnedObj.GetComponent<PlayerStatus>(); // if (status != null) // { // status.Health = objData.currentHealth; // } } else { Debug.LogWarning($"未找到对应预制体:{objData.prefabUniqueId}"); } } Debug.Log($"游戏加载完成!共恢复了 {_currentLoadedSaveData.savedObjects.Count} 个对象"); _currentLoadedSaveData = null; }
4. 额外优化建议
- 预制体标识优化:不要用名字当唯一标识,给每个预制体加一个带Guid的自定义组件,避免重名问题。
- 场景原有对象修改:如果要保存场景原本就有的对象(比如门的开关状态),给这些对象加
SaveableObject组件,在保存时读取组件内的状态,加载时恢复。 - 存储方式优化:如果保存的数据量大,建议用
File.WriteAllText把JSON写到Application.persistentDataPath下的本地文件,比PlayerPrefs更稳定。
内容的提问来源于stack exchange,提问作者Meher
相关产品推荐
相关产品推荐

