Unity+C#停车场游戏Level Manager实现咨询:高效存储关卡状态
停车场游戏Level Manager实现方案
为什么放弃PlayerPrefs?
PlayerPrefs仅适合存储零散的键值对,当关卡数量增多时,需要维护大量类似"Level_1_Unlocked"的键,管理混乱且扩展性差,结构化数据存储能力不足,不适合关卡状态的批量管理。
核心实现思路
采用结构化数据模型+本地Json持久化的方案,通过ScriptableObject在内存中管理关卡状态,序列化到本地文件实现持久化,兼顾易用性和可维护性。
1. 定义关卡数据结构
先创建可序列化的LevelData类,封装每个关卡的核心状态:
[System.Serializable] public class LevelData { public int levelId; // 关卡ID public bool isUnlocked; // 是否解锁 public bool isCurrent; // 是否为当前选中关卡 public float bestTime; // 可选:关卡最佳完成时间(按需扩展) }
2. 内存数据容器(ScriptableObject)
用ScriptableObject作为内存中的关卡数据容器,方便在编辑器中预览,也能快速对接逻辑:
using UnityEngine; using System.Collections.Generic; [CreateAssetMenu(fileName = "LevelSaveData", menuName = "Game/Level Save Data")] public class LevelSaveDataSO : ScriptableObject { public List<LevelData> allLevels = new List<LevelData>(); }
编辑器操作:右键菜单
Game/Level Save Data创建实例,拖到LevelManager的对应字段中。
3. LevelManager核心逻辑(单例模式)
实现全局可调用的单例管理器,负责数据加载、保存、状态更新:
using UnityEngine; using System.IO; using System.Collections.Generic; public class LevelManager : MonoBehaviour { public static LevelManager Instance; [SerializeField] private LevelSaveDataSO levelSaveData; private string saveFilePath; private const int TotalLevels = 15; // 替换为你的实际总关卡数 private void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); saveFilePath = Path.Combine(Application.persistentDataPath, "LevelSaveData.json"); LoadLevelData(); } else { Destroy(gameObject); } } // 初始化关卡数据(首次启动或新增关卡时调用) private void InitializeLevelData() { levelSaveData.allLevels.Clear(); for (int i = 1; i <= TotalLevels; i++) { LevelData level = new LevelData(); level.levelId = i; level.isUnlocked = i == 1; // 默认第一关解锁 level.isCurrent = i == 1; // 默认第一关为当前关卡 level.bestTime = float.MaxValue; levelSaveData.allLevels.Add(level); } SaveLevelData(); } // 加载本地存储的关卡数据 private void LoadLevelData() { if (File.Exists(saveFilePath)) { string json = File.ReadAllText(saveFilePath); List<LevelData> loadedData = JsonUtility.FromJson<List<LevelData>>(json); levelSaveData.allLevels = loadedData; // 适配新增关卡:如果存储的关卡数少于总关卡,自动补充 if (loadedData.Count < TotalLevels) { for (int i = loadedData.Count + 1; i <= TotalLevels; i++) { LevelData newLevel = new LevelData(); newLevel.levelId = i; newLevel.isUnlocked = false; newLevel.isCurrent = false; newLevel.bestTime = float.MaxValue; levelSaveData.allLevels.Add(newLevel); } SaveLevelData(); } } else { InitializeLevelData(); } } // 保存关卡数据到本地文件 public void SaveLevelData() { string json = JsonUtility.ToJson(levelSaveData.allLevels); File.WriteAllText(saveFilePath, json); } // 获取指定关卡的状态 public LevelData GetLevelData(int levelId) { return levelSaveData.allLevels.Find(l => l.levelId == levelId); } // 解锁当前关卡的下一关 public void UnlockNextLevel(int currentLevelId) { LevelData nextLevel = GetLevelData(currentLevelId + 1); if (nextLevel != null && !nextLevel.isUnlocked) { nextLevel.isUnlocked = true; SaveLevelData(); } } // 设置当前选中关卡 public void SetCurrentLevel(int levelId) { foreach (var level in levelSaveData.allLevels) { level.isCurrent = level.levelId == levelId; } SaveLevelData(); } // 获取当前选中的关卡ID public int GetCurrentLevelId() { LevelData currentLevel = levelSaveData.allLevels.Find(l => l.isCurrent); return currentLevel != null ? currentLevel.levelId : 1; } }
4. 常用调用示例
- 完成关卡后解锁下一关:
// 假设当前关卡ID为3 LevelManager.Instance.UnlockNextLevel(3); - 切换到指定关卡:
LevelManager.Instance.SetCurrentLevel(5); - 获取当前关卡状态:
LevelData currentLevel = LevelManager.Instance.GetLevelData(LevelManager.Instance.GetCurrentLevelId()); Debug.Log($"当前关卡:{currentLevel.levelId},是否解锁:{currentLevel.isUnlocked}");
内容的提问来源于stack exchange,提问作者Ak Khan
相关产品推荐
相关产品推荐

