Unity存档系统无报错但int数组inventoryIDs无法正常存读
其他玩家属性、位置float数组存读正常,说明二进制读写流程、PlayerData类的序列化标记、文件IO权限均无全局问题,故障仅出在inventoryIDs数组的定义、存盘同步、读盘回填三个环节,按以下顺序排查即可:
1. 检查PlayerData类的数组定义
两个高频低级错误会直接导致数组不被序列化:
- 给
inventoryIDs字段加了[NonSerialized]特性,BinaryFormatter会直接跳过该字段的序列化 - 数组初始化时写死长度为0(比如
public int[] inventoryIDs = new int[0];),后续同步数据时未重新赋值数组引用,写入的永远是空数组
正确定义参考:
[System.Serializable] public class PlayerData { public float health; public float thirst; public float hunger; public float oxygen; public float[] position; // 不要加NonSerialized特性,不要写死固定空长度 public int[] inventoryIDs; }
额外注意:BinaryFormatter默认不序列化C#自动属性,如果inventoryIDs是public int[] inventoryIDs { get; set; }这种属性写法,要改成普通public字段,或者加序列化支持。
2. 检查存盘前的数组同步逻辑
不要在Update()里逐帧修改数组元素却不更新数组引用:BinaryFormatter序列化时抓的是字段引用指向的内存块,如果背包槽位数量变动、或者你修改的是其他临时数组的元素,PlayerData持有的inventoryIDs引用没有更新,存进去的永远是旧值/空值。
正确的同步逻辑要在每次触发存盘时重新构建数组:
// 存盘触发时执行,不要仅在Update中逐元素赋值 List<int> tempInvIdList = new List<int>(); foreach (var slot in inventorySlots) { tempInvIdList.Add(slot.currentItemId); } // 重新赋值数组引用,确保PlayerData持有最新完整数据 playerData.inventoryIDs = tempInvIdList.ToArray();
3. 检查读盘后的数组回填逻辑
绝大多数“加载失败但不报错”的问题出在这里:回填时没做长度校验,读出来的数组长度和当前场景初始化的背包槽位数量不匹配,循环赋值时触发数组越界,而异常被全局try-catch吞掉没有打印日志,看起来就像没存上。
正确回填逻辑:
if (playerData.inventoryIDs != null) { // 取数组和槽位的最小长度,避免越界 int validLoadCount = Mathf.Min(playerData.inventoryIDs.Length, inventorySlots.Count); for (int i = 0; i < validLoadCount; i++) { inventorySlots[i].SetItem(playerData.inventoryIDs[i]); } // 超出存档数据长度的槽位清空,避免残留运行时旧数据 for (int i = validLoadCount; i < inventorySlots.Count; i++) { inventorySlots[i].ClearSlot(); } }
4. 兼容旧存档的特殊情况
如果你开发过程中改过inventoryIDs的字段名、或者把字段类型从int[]改成List<int>又改回来,旧存档文件里的字段签名和当前代码不匹配,BinaryFormatter会自动忽略不匹配的字段且不抛出异常。这种情况直接删掉Application.persistentDataPath路径下的旧存档文件,重新执行一次存读流程即可验证。
内容的提问来源于stack exchange,提问作者TTSI

