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

Unity跨会话保存数据:复杂对象存储问题及优化方案求助

问题描述

我正在开发一款游戏,需要跨会话保存玩家进度。目前我使用以下方法保存PlayerState类:

// Get the user data path
string userDataPath = Path.Combine(Application.persistentDataPath, "userState_" + username + ".json");

// Convert to JSON and save to file
string playerData = JsonUtility.ToJson(this);
File.WriteAllText(userDataPath, playerData);

该方法在处理字符串、整数等简单类型(如用户名、等级、金币)时运行正常,但保存装备、英雄等复杂类时出现问题。例如我的EquipmentData类如下:

using UnityEngine;

public enum EquipmentType { Armour, Weapon, Medalion, Horse, Cloak, Book, Horn};
public enum EquipmentGrade { Blue, Green, Yellow, Red, Purple, Golden };

[System.Serializable]
public class EquipmentData : MonoBehaviour
{
    public Sprite sprite;
    public string equipmentName;

    public EquipmentType equipmentType;
    public EquipmentGrade equipmentGrade;
    [Range(0f, 1f)] public float dropRate;
    [HideInInspector] public int ownedFragments;
    public int fragmentsToSynthesize;
    public int valorAttack;
    public int valorDefense;
    public int spiritAttack;
    public int spiritDefense;
    public int elementAttack;
    public int elementDefense;
    public int troopBase;
}

玩家可强化装备的至少7项属性,每件装备最多有10项可修改属性,且背包中可能存在数百件不同等级的装备。我尝试将包含挂载EquipmentData的GameObject列表的inventory添加到PlayerState中,但跨会话保存无效:

[HideInInspector] [SerializeField] public List<GameObject> inventory;

Unity仅会在会话内保存instanceID,重启后无法使用。

我曾考虑拆分到不同JSON文件(如username_equipment.json),但文件体积过大,序列化/反序列化繁琐,且后续更新装备、英雄等内容时维护难度高。

现提出两个问题:

  1. 能否在Unity中保存GameObject或类以实现跨会话复用?
  2. 若不可行,如何更优地保存这类大量数据?
解决方案

问题1:能否在Unity中保存GameObject或类以实现跨会话复用?

直接保存GameObject不可行——Unity序列化GameObject时仅存储会话内有效的实例ID、引用关系等数据,重启后这些ID完全失效,无法重建对象。

但可以保存纯数据类(不继承MonoBehaviour的可序列化类),这是Unity跨会话保存复杂数据的标准做法。你当前的EquipmentData继承了MonoBehaviour,会被当作组件而非纯数据处理,序列化时会包含大量Unity内部组件信息,干扰数据保存。

问题2:更优的大量数据保存方案

针对你的装备系统,推荐以下优化方案:

1. 重构数据类,分离逻辑与数据

将EquipmentData改为纯数据类,移除MonoBehaviour继承,保留[System.Serializable]特性:

using UnityEngine;

public enum EquipmentType { Armour, Weapon, Medalion, Horse, Cloak, Book, Horn};
public enum EquipmentGrade { Blue, Green, Yellow, Red, Purple, Golden };

[System.Serializable]
public class EquipmentData
{
    // 用Sprite的资源路径代替Sprite对象,加载时通过路径读取
    public string spritePath;
    public string equipmentName;

    public EquipmentType equipmentType;
    public EquipmentGrade equipmentGrade;
    public float dropRate;
    public int ownedFragments;
    public int fragmentsToSynthesize;
    public int valorAttack;
    public int valorDefense;
    public int spiritAttack;
    public int spiritDefense;
    public int elementAttack;
    public int elementDefense;
    public int troopBase;
    // 新增强化等级等动态属性
    public int强化Level;
}

注意:Sprite无法直接序列化,改用资源路径(如"Sprites/Equipment/Armour_01"),加载时通过Resources.Load<Sprite>(spritePath)重建引用。

2. 调整PlayerState的Inventory结构

将List<GameObject>替换为List<EquipmentData>,直接存储数据而非游戏对象:

[HideInInspector] [SerializeField] public List<EquipmentData> inventory;

这样JsonUtility.ToJson()可直接序列化整个装备列表,不会丢失数据。

3. 优化JSON存储方式

  • 单文件分块存储:无需拆分多个文件,在PlayerState中嵌套不同数据模块(如PlayerEquipmentData、PlayerHeroData),保持单文件的同时让结构清晰:
[System.Serializable]
public class PlayerState
{
    public string username;
    public int level;
    public int gold;
    public PlayerEquipmentData equipmentData;
    public PlayerHeroData heroData;
}

[System.Serializable]
public class PlayerEquipmentData
{
    public List<EquipmentData> inventory;
    public List<EquipmentData> equippedItems;
}
  • 压缩数据:若文件体积过大,可将JSON字符串压缩后再写入文件,读取时先解压:
// 保存时压缩
string json = JsonUtility.ToJson(playerState);
byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json);
using (var ms = new System.IO.MemoryStream())
{
    using (var gzip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress))
    {
        gzip.Write(jsonBytes, 0, jsonBytes.Length);
    }
    File.WriteAllBytes(userDataPath, ms.ToArray());
}

// 读取时解压
byte[] compressedBytes = File.ReadAllBytes(userDataPath);
using (var ms = new System.IO.MemoryStream(compressedBytes))
{
    using (var gzip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress))
    {
        using (var reader = new System.IO.StreamReader(gzip))
        {
            string json = reader.ReadToEnd();
            JsonUtility.FromJsonOverwrite(json, playerState);
        }
    }
}

4. 可选:使用更强大的序列化库

如果JsonUtility功能不足(如不支持字典、复杂嵌套结构),可使用Unity官方集成的Newtonsoft.Json(在Package Manager中搜索安装),它对复杂数据结构的序列化支持更好,API与JsonUtility类似,迁移成本低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 21:09:59