Unity游戏场景状态保存实现:重访场景保留离开时状态
Unity场景保持离开时状态的实现方案
一、避免卸载场景(最直接方案)
如果内存允许,切换场景时不卸载原场景,仅禁用它;返回时重新激活即可保留所有状态。
实现代码
// 切换场景并保留当前场景(叠加加载) public void SwitchSceneKeepCurrent(string targetSceneName) { // 以叠加模式加载目标场景 SceneManager.LoadScene(targetSceneName, LoadSceneMode.Additive); // 切换活跃场景到新场景,同时禁用原场景 Scene currentScene = SceneManager.GetActiveScene(); SceneManager.SetActiveScene(SceneManager.GetSceneByName(targetSceneName)); } // 返回之前的场景 public void ReturnToPreviousScene(string previousSceneName) { Scene targetScene = SceneManager.GetSceneByName(previousSceneName); if (targetScene.isLoaded) { // 卸载当前活跃场景 SceneManager.UnloadSceneAsync(SceneManager.GetActiveScene()); // 激活之前保留的场景 SceneManager.SetActiveScene(targetScene); } }
注意:此方法适合中小体量场景,缺点是会增加内存占用。
二、序列化存储场景状态(适合资源占用高的场景)
通过序列化保存场景中关键对象的状态(敌人存活、物品收集等),重新加载场景时读取并恢复。
1. 定义状态数据类
[Serializable] public class SceneStateData { public string sceneName; public Dictionary<string, bool> enemyAliveStatus; // Key:敌人唯一ID,Value:是否存活 public Dictionary<string, bool> itemCollectedStatus; // Key:物品唯一ID,Value:是否已收集 // 可扩展其他状态:门的开关、机关状态等 }
需给Enemy、CollectibleItem等对象添加UniqueID字段(Inspector手动赋值或自动生成)。
2. 离开场景前保存状态
public void SaveSceneState(string sceneName) { SceneStateData state = new SceneStateData(); state.sceneName = sceneName; state.enemyAliveStatus = new Dictionary<string, bool>(); state.itemCollectedStatus = new Dictionary<string, bool>(); // 收集敌人状态 Enemy[] enemies = FindObjectsOfType<Enemy>(); foreach (Enemy enemy in enemies) { state.enemyAliveStatus[enemy.UniqueID] = enemy.isAlive; } // 收集物品状态 CollectibleItem[] items = FindObjectsOfType<CollectibleItem>(); foreach (CollectibleItem item in items) { state.itemCollectedStatus[item.UniqueID] = item.isCollected; } // 序列化保存到PlayerPrefs(也可改用文件、数据库) string json = JsonUtility.ToJson(state); PlayerPrefs.SetString($"SceneState_{sceneName}", json); PlayerPrefs.Save(); }
3. 加载场景后恢复状态
public void LoadSceneState(string sceneName) { if (!PlayerPrefs.HasKey($"SceneState_{sceneName}")) return; string json = PlayerPrefs.GetString($"SceneState_{sceneName}"); SceneStateData state = JsonUtility.FromJson<SceneStateData>(json); // 恢复敌人状态 Enemy[] enemies = FindObjectsOfType<Enemy>(); foreach (Enemy enemy in enemies) { if (state.enemyAliveStatus.TryGetValue(enemy.UniqueID, out bool isAlive)) { enemy.isAlive = isAlive; enemy.gameObject.SetActive(isAlive); } } // 恢复物品状态 CollectibleItem[] items = FindObjectsOfType<CollectibleItem>(); foreach (CollectibleItem item in items) { if (state.itemCollectedStatus.TryGetValue(item.UniqueID, out bool isCollected)) { item.isCollected = isCollected; item.gameObject.SetActive(!isCollected); // 已收集物品隐藏 } } }
三、全局状态管理器(内存内持久化)
创建单例状态管理器,用DontDestroyOnLoad标记使其跨场景保留,负责存储所有场景的运行时状态。
实现代码
public class SceneStateManager : MonoBehaviour { public static SceneStateManager Instance; private Dictionary<string, SceneStateData> _allSceneStates = new Dictionary<string, SceneStateData>(); private void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } // 保存指定场景状态 public void SaveSceneState(string sceneName) { SceneStateData state = new SceneStateData(); state.sceneName = sceneName; // 此处复用前面的状态收集逻辑 _allSceneStates[sceneName] = state; } // 获取指定场景状态 public SceneStateData GetSceneState(string sceneName) { _allSceneStates.TryGetValue(sceneName, out SceneStateData state); return state; } }
说明:该方案状态仅存于内存,游戏退出后需额外写入持久化存储(如PlayerPrefs)才能保留。
四、方案选择建议
- 内存充足时优先用场景叠加加载,无需额外状态逻辑;
- 内存紧张或需要跨游戏会话保留状态时,用序列化存储方案;
- 仅需运行时跨场景保留状态,用全局状态管理器。
内容的提问来源于stack exchange,提问作者Yui
相关产品推荐
相关产品推荐

