[P0][CR-B2] 地面物品两份存储且按 32px 距离删除:捡 A 删 B、存档复制、读档不重建 #522
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
问题描述
地面物品同时存在两份:
engine.ground[]:用于模拟和存档;engine.groundItems:用于渲染和交互。捡起物品后,要从
ground[]里删掉对应的那一项,但匹配规则是"32px 以内或对象相同的第一件"。而同一次爆装的物品最小间距只有 16px,所以经常删掉旁边的另一件。被捡的那件还留在
ground[]里,存档时就多复制了一份。读档时又只替换ground[]、不重建groundItems,渲染与状态就对不上了。证据
findIndex(g => Math.hypot(...) <= 32 || g.item === entity.item)(engine.ts:1656)。findIndex按数组顺序返回第一个满足任一条件的元素。只要排在前面的某件物品落在 32px 内,就会先命中它,而不是后面那件真正被捡起的物品。groundItems.find(item) ?? findAtPoint(x, y, 48),找不到就退到 48px 范围)minSpacing = 16(ground-items.ts:1020)。Date.now()拼接(ground-items.ts:1372),也不进快照。loadSnapshot只替换ground(engine.ts:1202)。根因
渲染层需要的额外字段(弹跳动画、标签)没有挂在模拟层的物品上,而是另建了一份集合。两份集合之间没有稳定的主键,只能用坐标去"猜"谁对应谁。
修复指南
GameEngine只保留一份ground: Map<GroundId, GroundItem>(或者按插入顺序的数组加 id 索引)。模拟、拾取和存档都用它。Map<GroundId, GroundItemView>,按 id 关联,不参与模拟。nextGroundId++生成 id,并纳入快照。不要用Date.now()或随机数(与 #523 一致)。removeGround(id);删除不存在的 id 直接抛错,不要"找不到就删最近的"。pickup(groundId))。UI 先通过命中测试拿到 id,再下发命令。loadSnapshot之后,由前端按引擎的ground重建 view(或者由引擎发一个groundReset事件)。不要在引擎里维护渲染集合。验收标准
ground里只少了 A,B 仍在;存档、读档后数量正确。ground一一对应(id 集合相等)。src/中不再有基于Math.hypot(...) <= 32的删除逻辑。npm run typecheck0 error;npx vitest run全部通过。相关
已在提交
231d375(fix(ground): unify authoritative ground-item store with exact instance IDs (#522))中完成修复,并在5de27f8中补充端到端与 136 关全量验证套件。修复与验证摘要
ground_item_${seq})1:1 统一GroundItemManager与engine.ground,移除全部 5 处Math.hypot <= 32距离删除逻辑,改为按精确 ID 删除,并修复存档重复序列化与读档地面物品重建。tests/p0-522-ground-items.test.tsnpm run build0 错误、14 个 P0/E2E 测试套件(190/190 用例)100% 通过、全仓 Vitest 6,460/6,460 通过、136/136 关无头浏览器巡检通过。