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
相关产品推荐
相关产品推荐

