You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 15:07:19