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

Unity中Newtonsoft JSON加载玩家数据时IEnumerable集合丢失问题

问题分析与解决方案

你的问题核心出在**PlayerState中InventoryContent的IEnumerable<ItemSavableState>类型**以及反序列化时的异常处理逻辑上,具体原因和解决步骤如下:

1. IEnumerable接口的序列化/反序列化局限性

Newtonsoft.Json对IEnumerable<T>这种抽象集合类型的处理存在隐患:

  • 序列化时,IEnumerable会被当作枚举器处理,但反序列化时,Newtonsoft需要选择一个具体的集合类型实例化。如果原集合是只读枚举(比如Enumerable.Empty)或LINQ查询结果,反序列化后的实例可能是一次性枚举器,后续访问时已被耗尽,导致InventoryContent看起来为空,最终重新保存成空数组。
  • 抽象集合类型无法保证反序列化后的实例具备可重复遍历、可再次序列化的特性。

解决方法:改用具体集合类型

把PlayerState中的InventoryContent类型从IEnumerable<ItemSavableState>改成List<ItemSavableState>:

public sealed class PlayerState
{
    public static PlayerState Default => new(100f, 0f, PlayerShipType.TempestMk1, new List<ItemSavableState>());

    public float Credits { get; }
    public float Experience { get; }
    public PlayerShipType SelectedShip { get; }
    public List<ItemSavableState> InventoryContent { get; }

    public PlayerState(float credits,
                       float experience,
                       PlayerShipType selectedShip,
                       List<ItemSavableState> inventoryContent)
    {
        Credits = Mathf.Clamp(credits, 0f, float.MaxValue);
        Experience = Mathf.Clamp(experience, 0f, float.MaxValue);
        SelectedShip = selectedShip;
        InventoryContent = inventoryContent ?? throw new ArgumentNullException(nameof(inventoryContent));
    }
}

同时在GetState()中把枚举转换为List:

public string GetState()
{
    var inventoryStates = Inventory.GetItemsSavableStates().ToList();
    PlayerState state = new(Wallet.Balance, Experience.Value, SelectedShipType, inventoryStates);
    return JsonConvert.SerializeObject(state, Formatting.Indented, s_serializationSettings);
}

2. 异常吞导致的问题排查障碍

你的SetState()方法直接吞掉所有异常,无法得知反序列化失败的具体原因——比如子类类型绑定错误、集合实例化失败、ItemSavableState子类构造函数参数不匹配等。这会导致你误以为是集合类型问题,但实际可能是**DefaultSerializationBinder无法正确解析子类类型**。

解决方法:添加异常日志

修改catch块,记录异常详情,方便定位问题:

catch (Exception ex)
{
    Debug.LogError($"加载玩家存档失败: {ex.Message}\n{ex.StackTrace}");
    PlayerState defaultState = PlayerState.Default;
    Wallet = new(defaultState.Credits);
    SelectedShipType = defaultState.SelectedShip;
}

3. 序列化绑定器与子类类型验证

检查DefaultSerializationBinder是否能正确解析RegularAmmoSetSavableState类型:

  • Unity中默认程序集名称通常是Assembly-CSharp而非Scripts,可以通过typeof(RegularAmmoSetSavableState).Assembly.FullName获取正确的程序集名称。
  • 优化绑定器,增加容错性:
public sealed class DefaultSerializationBinder : ISerializationBinder
{
    public Type BindToType(string assemblyName, string typeName)
    {
        var type = Type.GetType($"{typeName}, {assemblyName}");
        if (type == null)
        {
            // 尝试Unity默认程序集
            type = Type.GetType($"{typeName}, Assembly-CSharp");
        }
        return type ?? throw new InvalidOperationException($"无法找到类型: {typeName}, {assemblyName}");
    }

    public void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        assemblyName = serializedType.Assembly.FullName;
        typeName = serializedType.FullName;
    }
}

4. 确保子类的反序列化兼容性

RegularAmmoSetSavableState作为ItemSavableState的子类,需要确保:

  • 所有需要反序列化的属性都标注[JsonProperty]。
  • 构造函数添加[JsonConstructor]标注,确保Newtonsoft能正确匹配参数:
public sealed class RegularAmmoSetSavableState : ItemSavableState
{
    [JsonProperty]
    public int Amount { get; }
    [JsonProperty]
    public float HeatGeneration { get; }
    [JsonProperty]
    public float Speed { get; }
    [JsonProperty]
    public float Damage { get; }

    [JsonConstructor]
    public RegularAmmoSetSavableState(int amount, float heatGeneration, float speed, float damage, Size size, Quality quality, float price)
        : base(size, quality, price)
    {
        Amount = amount;
        HeatGeneration = heatGeneration;
        Speed = speed;
        Damage = damage;
    }

    public override IItem Recreate(SavedItemsServices services)
    {
        // 实现逻辑
    }
}

总结

优先修改InventoryContent为List<ItemSavableState>,同时添加异常日志排查具体错误,再验证类型绑定和子类的反序列化逻辑,就能解决存档丢失和报错的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:53:09