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);
}
所以我试图让物品在库存中具有相同 ID 的其他物品之后进行检查,然后检查当前堆栈的数量以确保每个堆栈的最大数量为 99,然后将其他堆栈的数量添加到第一个堆栈,然后删除转移的金额,之后它会检查库存,以便如果任何堆栈的金额为 0,则会将其删除,但由于某种原因,它会以每个堆栈都为 0 结束。
我尝试过一个循环,其中每个堆栈检查除自身之外的所有堆栈,我尝试过一个循环,其中每个堆栈检查列表中自身之上的所有堆栈,然后图像是我最新的尝试,但没有任何效果。
我认为在第一个循环中,您正在相互检查每个库存。
您唯一要跳过的是
x != i
但我相信你宁愿只检查那些你还没有见过的条目,所以例如只从以下条目开始:
for(var i = x + 1; i < Inventory.Count; i++)
因此,假设前面的项目的情况已经由
x
的先前迭代处理过,则您只执行“向前看”而不是“向后看”。
然后,一旦到达
99
,您将根据indexes
跳过具有相同索引的任何其他条目(顺便说一句,正确的复数是indices
)。这也是不正确的。
您仍然想使用相同的 ID 检查以下条目
在最后,如此处所述,您的循环会跳过列表末尾的项目,因为当您迭代列表并从中删除项目时,列表计数会减少。
但是,您不需要任何
Linq
也不需要创建新的列表实例。
List<T>.RemoveAll
完美满足您的目的:
Inventory.RemoveAll(item => item.amount <= 0);
总的来说,我想它应该是这样的
private void UpdateInventory()
{
for (var x = 0; x < Inventory.Count; x++)
{
var current = Inventory[x];
// only deal for entries that have items - the 0 you can already skip right away as it will be removed in the end
// and 99 are already full
if (current.amount > 0 && current.amount < 99)
{
// only check items after the current one
for (var i = x + 1; i < Inventory.Count; i++)
{
var other = Inventory[i];
if (other.ID == current.ID && other.amount > 0)
{
if (current.amount + other.amount > 99)
{
other.amount -= 99 - current.amount;
current.amount = 99;
// in this case you can immediately exit the inner loop since your slot is already full!
break;
}
current.amount += other.amount;
other.amount = 0;
}
}
}
}
// remove all 0 entries
Inventory.RemoveAll(item => item.amount <= 0);
}
第 144-150 行中的循环在迭代列表时从列表中删除项目,这就是剩余项目的原因。 让我们看一下更简单的伪代码并运行它:
var list = {1,2,3,4,5};
for (int i = 0; i < list.Count; i++) {
if (list[i] >= 0) {
list.RemoveAt(i);
}
}
现在让我们运行代码 -
i = 0
list = {1,2,3,4,5};
list[i] = 1 // >=0 , so it is removed
list = {2,3,4,5}
i = 1
list[i] = 3 // we skipped 2!!!!
因为您在修改列表时迭代列表,所以您跳过了项目。 避免这种情况的最简单方法是,不从列表中删除项目,而是创建一个仅包含您要保留的项目的新列表。您可以使用 Linq 的
Where
函数,或显式执行此操作。
无论如何,最好的做法是将逻辑分成更小的块,并通过单元测试单独测试每个块,以便了解代码出了什么问题。
也许你应该尝试以不同的方式思考这个问题。您的代码示例有点混乱,我不想成为在它损坏时必须尝试调试它的编码员。 (讽刺的是,这正是你要求人们做的事情......)
让我们这样想:角色的库存中有 N 个物品,并且您希望在添加新物品时聚合它们。每个项目都有一个堆栈限制,虽然您没有明确说明,但我假设角色库存中的项目堆栈数量是固定的。
与其尝试管理内存中的堆栈(这看起来像皇家 PITA),不如重新考虑如何存储数据:
public enum eItemType
{
Arrow = 1,
Bullet = 2,
Scroll = 3,
//etc
}
private Dictionary<eItemType,int> _Inventory = new();
有了这个,任何添加或删除都是简单的字典查找,并且进行数学运算也很简单,否则可能会跨越堆栈。要确定在屏幕上渲染多少个堆栈,您可以循环遍历集合并渲染字典值/99 并向上舍入以获得堆栈计数。枚举只是锦上添花,使阅读代码中的逻辑检查变得简单:
if (_Inventory[eItemType.Arrow] > 1)
{
// do stuff here
}
不要将渲染多少堆栈的问题与将数据存储在内存中的问题混为一谈。他们没有联系。