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

Unity C#序列化嵌套对象异常:子对象被序列化为True而非实际对象

解决Unity中IFormatter序列化嵌套ScriptableObject时变为"true"的问题

这个问题我之前也踩过坑——本质上是因为**.NET的IFormatter(比如BinaryFormatter这类)无法正确识别Unity的ScriptableObject类型**。你看到的"true"根本不是Inventory的实际数据,而是Unity内部对ScriptableObject引用的一个标记,默认序列化器完全没把Inventory的业务内容写进去,只记录了“这个引用存在”的布尔值,反序列化时自然会因为类型不匹配报错。

下面给你两种可行的解决思路:


方案一:序列化ScriptableObject的数据内容而非引用

ScriptableObject是Unity特有的资源型对象,.NET原生序列化器不理解它的内部结构。所以最优解是不要直接序列化Inventory对象本身,而是把它里面的具体业务数据单独提取出来序列化,反序列化时再重新赋值给新的Inventory实例。

举个实际例子,假设你的Inventory类结构是这样的:

[CreateAssetMenu(fileName = "New Inventory", menuName = "Inventory/Inventory")]
[System.Serializable]
public class Inventory : ScriptableObject {
    public List<ItemData> items; // 自定义的可序列化物品数据类
    public int currentGold;
    // 其他成员方法省略
}

那你需要修改SavePlayerObjectSerializationSurrogate的逻辑,手动处理Inventory的字段:

序列化逻辑

public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
    SaveObject saveObject = (SaveObject)obj;
    // 不要直接Add整个Inventory对象
    info.AddValue("SceneName", saveObject.sceneName);
    
    var inventory = saveObject.PlayerInventory;
    // 单独序列化Inventory里的每个可序列化字段
    info.AddValue("InventoryItems", inventory.items);
    info.AddValue("InventoryGold", inventory.currentGold);
}

反序列化逻辑

public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
    SaveObject saveObject = (SaveObject)obj;
    saveObject.sceneName = info.GetString("SceneName");
    
    // 运行时创建新的Inventory实例(如果是Project资源,改用AssetDatabase加载)
    var newInventory = ScriptableObject.CreateInstance<Inventory>();
    newInventory.items = (List<ItemData>)info.GetValue("InventoryItems", typeof(List<ItemData>));
    newInventory.currentGold = info.GetInt32("InventoryGold");
    
    saveObject.PlayerInventory = newInventory;
    return saveObject;
}

方案二:改用Unity原生序列化方式

如果不想手动处理每个字段,也可以放弃IFormatter,用Unity专门为自身对象设计的序列化方案:

  • JsonUtility:直接把SaveObject转换成JSON字符串存储,它能完美识别标记了[Serializable]的ScriptableObject
  • BinaryFormatter+JsonUtility中转:先把Inventory转成JSON字符串,再序列化这个字符串,反序列化时再转成对象
  • 实现ISerializationCallbackReceiver:在ScriptableObject内部处理序列化前后的逻辑,确保数据能被正确持久化

比如用JsonUtility的极简示例:

// 保存数据
string saveJson = JsonUtility.ToJson(saveObject);
File.WriteAllText(Application.persistentDataPath + "/player_save.json", saveJson);

// 加载数据
string loadJson = File.ReadAllText(Application.persistentDataPath + "/player_save.json");
SaveObject loadedSave = ScriptableObject.CreateInstance<SaveObject>();
JsonUtility.FromJsonOverwrite(loadJson, loadedSave);

关键注意事项

  • 永远不要试图直接序列化Unity的Object子类(ScriptableObject、MonoBehaviour等)到.NET原生序列化器中,它们的生命周期和序列化逻辑由Unity管控,.NET序列化器无法正确处理
  • 如果你的Inventory是Project窗口里的资源文件,反序列化时不要用CreateInstance,而是用AssetDatabase.LoadAssetAtPath<Inventory>()加载原资源,再把序列化的数据赋值给它,避免创建无意义的新资源实例

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:17:26