我的物品堆叠算法异常:所有堆叠数量归零问题排查
背包堆叠合并功能故障排查
需求描述
实现背包物品堆叠合并:将同ID物品堆叠至最大99数量,把其他堆叠的物品转移到第一个同ID堆叠中,扣除对应数量,最后移除数量≤0的堆叠。但运行后所有堆叠数量均变为0。
已尝试的方案:
- 让每个堆叠检查除自身外的所有堆叠
- 让每个堆叠检查列表中自身上方的所有堆叠
- 当前代码为最新尝试
当前代码
private void UpdateInventory() { List<byte> indexes = new(); for (int x = 0; x < Inventory.Count; x++) { if (!indexes.Contains(Inventory[x].ID)) { indexes.Add(Inventory[x].ID); for (int i = x + 1; i < Inventory.Count; i++) { if (x != i && Inventory[x].ID == Inventory[i].ID && Inventory[x].amount < 99) { if (Inventory[x].amount + Inventory[i].amount > 99) { Inventory[i].amount -= 99 - Inventory[x].amount; Inventory[x].amount = 99; } else { Inventory[x].amount += Inventory[i].amount; Inventory[i].amount = 0; } } } } } Inventory.RemoveAll(item => item.amount <= 0); }
问题分析与修复
核心问题
- 引用类型意外修改:如果背包中的物品是引用类型,多个堆叠引用同一个对象实例时,修改其中一个的
amount会同步影响所有引用该对象的堆叠,导致所有同ID物品数量被错误清零。 - 循环逻辑冗余与遗漏:原始代码中
x != i判断完全冗余(i从x+1开始,永远不等于x);且堆叠填满后未正确处理剩余数量的后续堆叠。
修复方案
方案1:优化循环逻辑(贴合原始思路)
简化合并逻辑,明确计算剩余空间,只转移可容纳的数量,避免复杂条件判断:
private void UpdateInventory() { List<byte> processedIds = new(); for (int x = 0; x < Inventory.Count; x++) { var currentItem = Inventory[x]; // 跳过已处理ID或数量为0的物品 if (currentItem.amount <= 0 || processedIds.Contains(currentItem.ID)) continue; processedIds.Add(currentItem.ID); int remainingSpace = 99 - currentItem.amount; // 合并后续同ID物品到当前堆叠 for (int i = x + 1; i < Inventory.Count; i++) { var targetItem = Inventory[i]; if (targetItem.ID != currentItem.ID || targetItem.amount <= 0) continue; if (remainingSpace <= 0) break; // 当前堆叠已满,停止合并 // 计算可转移的最大数量 int transferAmount = Math.Min(remainingSpace, targetItem.amount); currentItem.amount += transferAmount; targetItem.amount -= transferAmount; remainingSpace -= transferAmount; } } Inventory.RemoveAll(item => item.amount <= 0); }
方案2:按ID分组合并(更简洁可靠)
先按ID分组,再对每组进行堆叠合并,逻辑更清晰:
private void UpdateInventory() { // 按ID分组,保留原始出现顺序 var groupedItems = Inventory.GroupBy(item => item.ID).ToList(); Inventory.Clear(); foreach (var group in groupedItems) { var items = group.ToList(); if (items.Count == 0) continue; // 以第一个堆叠为主堆叠 var mainItem = items[0]; int remainingCapacity = 99 - mainItem.amount; // 合并后续堆叠的数量 for (int i = 1; i < items.Count; i++) { var subItem = items[i]; if (subItem.amount <= 0) continue; if (remainingCapacity <= 0) { // 主堆叠已满,保留当前剩余堆叠 Inventory.Add(subItem); continue; } int transfer = Math.Min(remainingCapacity, subItem.amount); mainItem.amount += transfer; subItem.amount -= transfer; remainingCapacity -= transfer; } // 添加主堆叠(数量>0才保留) if (mainItem.amount > 0) { Inventory.Add(mainItem); } } Inventory.RemoveAll(item => item.amount <= 0); }
额外注意事项
如果物品是引用类型,添加到背包时务必创建独立实例,避免多个堆叠引用同一个对象,否则会出现修改一个影响全部的问题。
内容的提问来源于stack exchange,提问作者HampusMCK
相关产品推荐
相关产品推荐

