拾取物重生导致背包重复

2026-09-12 · unity

这篇记一个设计层面的冲突,而不只是代码 bug:“场景复位”这个机制,把”关卡物件”和”玩家进度”一视同仁地复位了。

现象

玩家死亡重生之后:

原因:接口遍历是”一视同仁”的

死亡重生的场景复位是这么做的(见死亡与重生):

foreach (MonoBehaviour mb in FindObjectsOfType<MonoBehaviour>())
    if (mb is IResettable r) r.ResetLevelObject();

而 Pickup 也实现了 IResettable:

public void ResetLevelObject()      //旧版:一律回到"未拾取"状态
{
    transform.position = startPos;
    sr.enabled = true;
    col.enabled = true;
    taken = false;
}

于是重生时,拾取物被当成”被消耗掉的关卡物件”复位了——它重新出现、再次可捡,而背包里那份还留着 → 重复。

根本冲突在这:

复位是正确的复位是错误的
坠落平台✅ 死亡后应回到原位,关卡才可重玩
拾取物❌ 它属于玩家进度,捡到就是捡到了,不该退回去

同一个接口、同一次遍历,两种对象需要的语义相反。所以判断必须下沉到组件自己身上。

修复:让组件自己回答”我该不该复位”

public void ResetLevelObject()
{
    //已经被拾取过(背包里还留着)→ 不再重新生成,否则能重复捡、背包出现多份
    if (Inventory.CountOf(item) > 0)
    {
        taken = true;
        sr.enabled = false;
        col.enabled = false;
        return;
    }

    transform.position = startPos;
    sr.enabled = true;
    col.enabled = true;
    taken = false;
}

逻辑很直白:背包里还有这个东西 → 说明它已经被捡走了 → 保持隐藏,别复活。

关于判断条件的一个取舍

这里用的是 Inventory.CountOf(item) > 0(当前背包里有几个),而不是新增一个 pickedBefore 布尔标记。

把这个判断写在这里、并在注释里说清它的前提,是为了将来改的时候知道该改哪。

小结