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

Unity运行时创建/保存自定义关卡数据的最佳方案咨询

关于Unity自定义关卡存档的方案建议

是否推荐用Scriptable Objects做运行时持久化?

不推荐。Scriptable Objects(SO)的设计定位是编辑器内的配置容器,虽然运行时可以实例化,但它并不适合做运行时的持久化存储:

  • SO内部包含Unity的对象引用元数据,JsonUtility无法正确序列化这些非纯数据的部分,导致序列化/反序列化后数据丢失或实例无法还原;
  • 运行时创建的SO实例在跨平台(尤其是移动端)的持久化兼容性上存在风险,相关调试资料少,后期维护成本高。

更优的存档系统实现方案

1. 纯数据类(POCO)+ JSON序列化(最适合当前需求)

直接定义仅包含关卡数据的普通C#类,配合JSON序列化工具保存到本地文件,完全不需要依赖MonoBehaviour或SO,轻量且优雅。

示例代码:
首先定义数据类:

[System.Serializable]
public class LevelData
{
    public string levelName;
    public string authorName;
}

// 用于包裹列表(解决JsonUtility无法直接序列化顶级数组的问题)
[System.Serializable]
public class LevelDataCollection
{
    public List<LevelData> levels;
}

然后实现保存/加载逻辑:

using UnityEngine;
using System.IO;
using System.Collections.Generic;

public class LevelSaveManager : MonoBehaviour
{
    private readonly string _saveFilePath = Path.Combine(Application.persistentDataPath, "custom_levels.json");

    // 保存关卡列表
    public void SaveCustomLevels(List<LevelData> levels)
    {
        var levelCollection = new LevelDataCollection { levels = levels };
        string jsonContent = JsonUtility.ToJson(levelCollection, prettyPrint: true);
        File.WriteAllText(_saveFilePath, jsonContent);
    }

    // 加载关卡列表
    public List<LevelData> LoadCustomLevels()
    {
        if (!File.Exists(_saveFilePath))
        {
            return new List<LevelData>();
        }

        string jsonContent = File.ReadAllText(_saveFilePath);
        var levelCollection = JsonUtility.FromJson<LevelDataCollection>(jsonContent);
        return levelCollection?.levels ?? new List<LevelData>();
    }
}

如果觉得JsonUtility的数组限制麻烦,可以使用Unity官方支持的**Newtonsoft Json(Json.NET)**包(通过Package Manager安装),它可以直接序列化/反序列化列表,代码更简洁:

using Newtonsoft.Json;

// 保存方法
public void SaveCustomLevels(List<LevelData> levels)
{
    string jsonContent = JsonConvert.SerializeObject(levels, Formatting.Indented);
    File.WriteAllText(_saveFilePath, jsonContent);
}

// 加载方法
public List<LevelData> LoadCustomLevels()
{
    if (!File.Exists(_saveFilePath))
    {
        return new List<LevelData>();
    }

    string jsonContent = File.ReadAllText(_saveFilePath);
    return JsonConvert.DeserializeObject<List<LevelData>>(jsonContent) ?? new List<LevelData>();
}

2. 本地数据库(适合未来需求扩展)

如果后续关卡数据会变得复杂(比如增加关卡ID、创建时间、关卡内容数据等),或者需要频繁查询、修改单条关卡数据,推荐使用SQLite本地数据库。

示例代码(基于sqlite-net插件):
首先定义数据模型:

public class LevelData
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public string LevelName { get; set; }
    public string AuthorName { get; set; }
}

然后实现数据库操作:

using SQLite;
using UnityEngine;
using System.IO;
using System.Collections.Generic;

public class LevelDbManager : MonoBehaviour
{
    private SQLiteConnection _dbConnection;

    void Awake()
    {
        string dbPath = Path.Combine(Application.persistentDataPath, "custom_levels.db");
        _dbConnection = new SQLiteConnection(dbPath);
        _dbConnection.CreateTable<LevelData>();
    }

    // 保存/更新单条关卡数据
    public void SaveLevel(LevelData level)
    {
        _dbConnection.InsertOrReplace(level);
    }

    // 加载所有关卡
    public List<LevelData> LoadAllLevels()
    {
        return _dbConnection.Table<LevelData>().ToList();
    }

    // 根据作者查询关卡
    public List<LevelData> LoadLevelsByAuthor(string authorName)
    {
        return _dbConnection.Table<LevelData>().Where(l => l.AuthorName == authorName).ToList();
    }

    void OnDestroy()
    {
        _dbConnection?.Close();
    }
}

3. PlayerPrefs(仅适合极少量数据)

如果自定义关卡数量极少,也可以用PlayerPrefs将序列化后的关卡列表字符串存储,但这种方式仅适合临时存储或数据量极小的场景,不推荐作为长期存档方案。


内容的提问来源于stack exchange,提问作者user8482543

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 07:40:33