fix(map-gen/collision): 未贴地砖的空白单元格(unstamped cells)在碰撞网格中被误判为可行走区域 #38

Closed
opened 2026-09-16 13:12:50 +00:00 by troytt · 3 comments
Owner

背景说明

在实现 M8 世界连通与全地图可达性校验(scripts/verify-world-walk.ts)过程中,发现 buildIsoMapScene(位于 src/game/d2map.ts)在构建 sub-tile 碰撞网格时存在一处底层缺陷:

  1. buildIsoMapScene 初始化 blocked = new Uint8Array(gridWidth * gridHeight) 时默认全为 0(即可行走 / walkable)。
  2. 仅当某个单元格(cell)中存在 tile 且其 subTileFlags.blockWalk 或 blockPlayerWalk 为真时,才将对应的 sub-tile 标记为 1(阻挡)。
  3. 后果:对于迷宫生成器(Maze DRLG)生成的地下城(如洞穴 Act 1 - Cave 2、墓穴、下水道等),未铺设任何房间 DS1 地板图块的岩石空白区(unstamped void cells)没有任何 tile,因此其 blocked 值保持为 0。这导致整张地图外围的虚空区域在碰撞网格中全部连通且“可行走”。

实测影响

  • 在 9-act-1-cave-2-var1(73×49 cells)上统计连通分量时,最大的可行走连通分量不是洞穴内部,而是洞穴外部未贴图的虚空区域(46,050 个 sub-tiles)。
  • 如果在迷宫地图上使用 largestWalkableRegion(grid) 过滤落脚点,会将楼梯/传送落脚点推向地图外的虚空,导致玩家穿墙或困死(因此在 pack-act-assets.ts 中不得不对 kind === 'maze' 禁用 largestWalkableRegion)。
  • findIsoSpawn 在某些边界情形下可能将出生点选在未贴地板的虚空中。
  • 若房间墙体存在 1 sub-tile 的对角缝隙,玩家可能从地下城内部泄漏到地图外围的虚空区域。

待实现功能清单 (TODOs)

  • 在 buildIsoMapScene(src/game/d2map.ts)中跟踪每个 cell / sub-tile 是否至少铺设了一层地面(floor)或可行走瓦片;对于没有任何 floor/tile 覆盖的空白单元格,默认将其 5×5 个 sub-tiles 标记为 blocked = 1。
  • 验证所有预设地图(Preset)、野外地图(Wilderness)与迷宫地图(Maze)在修复后不会误封正常过道或桥面。
  • 在 scripts/pack-act-assets.ts 的 buildSceneLinks 中解除 kind === 'maze' 对 largestWalkableRegion(grid) 的豁免限制,使迷宫地图同样享受主连通分量锚点吸附保护。
  • 在 scripts/verify-world-walk.ts 中对迷宫地图启用与野外/预设地图一致的全图主连通分量可达性断言。

关联代码位置

  • src/game/d2map.ts:buildIsoMapScene(L404-445,blocked 数组初始化与 stamp() 闭包)
  • scripts/pack-act-assets.ts:buildSceneLinks(L520-536,关于 kind === 'maze' 禁用 largestWalkableRegion 的注释说明)
  • scripts/verify-world-walk.ts:全图出入口可达性扫描器
## 背景说明 在实现 M8 世界连通与全地图可达性校验(`scripts/verify-world-walk.ts`)过程中,发现 `buildIsoMapScene`(位于 `src/game/d2map.ts`)在构建 sub-tile 碰撞网格时存在一处底层缺陷: 1. `buildIsoMapScene` 初始化 `blocked = new Uint8Array(gridWidth * gridHeight)` 时默认全为 `0`(即可行走 / walkable)。 2. 仅当某个单元格(cell)中存在 tile 且其 `subTileFlags.blockWalk` 或 `blockPlayerWalk` 为真时,才将对应的 sub-tile 标记为 `1`(阻挡)。 3. **后果**:对于迷宫生成器(Maze DRLG)生成的地下城(如洞穴 `Act 1 - Cave 2`、墓穴、下水道等),未铺设任何房间 DS1 地板图块的岩石空白区(unstamped void cells)没有任何 tile,因此其 `blocked` 值保持为 `0`。这导致整张地图外围的虚空区域在碰撞网格中全部连通且“可行走”。 ### 实测影响 - 在 `9-act-1-cave-2-var1`(73×49 cells)上统计连通分量时,最大的可行走连通分量不是洞穴内部,而是洞穴外部未贴图的虚空区域(46,050 个 sub-tiles)。 - 如果在迷宫地图上使用 `largestWalkableRegion(grid)` 过滤落脚点,会将楼梯/传送落脚点推向地图外的虚空,导致玩家穿墙或困死(因此在 `pack-act-assets.ts` 中不得不对 `kind === 'maze'` 禁用 `largestWalkableRegion`)。 - `findIsoSpawn` 在某些边界情形下可能将出生点选在未贴地板的虚空中。 - 若房间墙体存在 1 sub-tile 的对角缝隙,玩家可能从地下城内部泄漏到地图外围的虚空区域。 ## 待实现功能清单 (TODOs) - [ ] 在 `buildIsoMapScene`(`src/game/d2map.ts`)中跟踪每个 cell / sub-tile 是否至少铺设了一层地面(floor)或可行走瓦片;对于没有任何 floor/tile 覆盖的空白单元格,默认将其 5×5 个 sub-tiles 标记为 `blocked = 1`。 - [ ] 验证所有预设地图(Preset)、野外地图(Wilderness)与迷宫地图(Maze)在修复后不会误封正常过道或桥面。 - [ ] 在 `scripts/pack-act-assets.ts` 的 `buildSceneLinks` 中解除 `kind === 'maze'` 对 `largestWalkableRegion(grid)` 的豁免限制,使迷宫地图同样享受主连通分量锚点吸附保护。 - [ ] 在 `scripts/verify-world-walk.ts` 中对迷宫地图启用与野外/预设地图一致的全图主连通分量可达性断言。 ## 关联代码位置 - `src/game/d2map.ts`:`buildIsoMapScene`(L404-445,`blocked` 数组初始化与 `stamp()` 闭包) - `scripts/pack-act-assets.ts`:`buildSceneLinks`(L520-536,关于 `kind === 'maze'` 禁用 `largestWalkableRegion` 的注释说明) - `scripts/verify-world-walk.ts`:全图出入口可达性扫描器
Author
Owner

已在 src/game/d2map.ts 的 buildIsoMapScene 中完成修复:

  • 遍历单元格时,若某个单元格没有任何有效地板(floor.prop1 !== 0 && !floor.hidden)且没有任何有效墙体(wall.prop1 !== 0 && !wall.hidden),将其对应的 5×5 sub-tiles 全部置为 blocked = 1。
  • 在 scripts/pack-act-assets.ts 的 buildSceneLinks 中解除 kind === 'maze' 的豁免限制,对所有类型关卡(预设、野外、迷宫)统一启用 largestWalkableRegion(grid) 主连通分量过滤与全图跨度锚点吸附。
  • 经 scripts/verify-world-walk.ts 实测,此前因虚空泄漏导致兜底楼梯落在墙外的迷宫关卡(如 Cave 5、Barracks、Act 2 Sewer、Act 3 Dungeon 等)出入口配对可达率已提升至 100%,困死地图数降至 0。
已在 `src/game/d2map.ts` 的 `buildIsoMapScene` 中完成修复: - 遍历单元格时,若某个单元格没有任何有效地板(`floor.prop1 !== 0 && !floor.hidden`)且没有任何有效墙体(`wall.prop1 !== 0 && !wall.hidden`),将其对应的 5×5 sub-tiles 全部置为 `blocked = 1`。 - 在 `scripts/pack-act-assets.ts` 的 `buildSceneLinks` 中解除 `kind === 'maze'` 的豁免限制,对所有类型关卡(预设、野外、迷宫)统一启用 `largestWalkableRegion(grid)` 主连通分量过滤与全图跨度锚点吸附。 - 经 `scripts/verify-world-walk.ts` 实测,此前因虚空泄漏导致兜底楼梯落在墙外的迷宫关卡(如 `Cave 5`、`Barracks`、`Act 2 Sewer`、`Act 3 Dungeon` 等)出入口配对可达率已提升至 **100%**,困死地图数降至 **0**。
troytt reopened this issue 2026-09-16 13:38:46 +00:00
Author
Owner

补充实测结论:

  1. 若直接在 src/game/d2map.ts 的 buildIsoMapScene 中将无 floor/wall 的单元格置为 blocked = 1,会导致 scripts/verify-generators.ts 中 15 张地图(如 Act 2 - Arcane、Act 4 - Lava 1、Act 1 - Wilderness 3 等)的 walkableAreas 连通性检查回归失败(因为现有生成器验证依赖部分空单元格或跨块过渡)。因此 d2map.ts 保持原样,本 Issue 保持开启以跟踪后续生成器层面的 floor 铺设完整性。
  2. 针对烘焙产物中的迷宫楼梯/传送点落入虚空问题,已在 scripts/pack-act-assets.ts 的 buildSceneLinks 中通过 floorMaskedGrid(ds1, grid) 构造仅包含有效 floors 单元格的掩码网格来提取 largestWalkableRegion。这样既保证 verify-generators 754/754 100% 通过,又确保所有迷宫(Maze)的楼梯与兜底落脚点 100% 落在真实地下城房间地板内,彻底消除虚空困死现象。
补充实测结论: 1. 若直接在 `src/game/d2map.ts` 的 `buildIsoMapScene` 中将无 floor/wall 的单元格置为 `blocked = 1`,会导致 `scripts/verify-generators.ts` 中 15 张地图(如 `Act 2 - Arcane`、`Act 4 - Lava 1`、`Act 1 - Wilderness 3` 等)的 `walkableAreas` 连通性检查回归失败(因为现有生成器验证依赖部分空单元格或跨块过渡)。因此 `d2map.ts` 保持原样,本 Issue 保持开启以跟踪后续生成器层面的 floor 铺设完整性。 2. 针对烘焙产物中的迷宫楼梯/传送点落入虚空问题,已在 `scripts/pack-act-assets.ts` 的 `buildSceneLinks` 中通过 `floorMaskedGrid(ds1, grid)` 构造仅包含有效 `floors` 单元格的掩码网格来提取 `largestWalkableRegion`。这样既保证 `verify-generators` **754/754 100% 通过**,又确保所有迷宫(Maze)的楼梯与兜底落脚点 100% 落在真实地下城房间地板内,彻底消除虚空困死现象。
Author
Owner

遵照指示暂时关闭:目前已在 scripts/pack-act-assets.ts 中通过地面有效掩码(floorMaskedGrid)成功规避了迷宫楼梯和落脚点掉入虚空的问题,满足现阶段连通与可用性诉求;底层全局碰撞网格的空白单元格阻挡策略暂不作进一步调整,先行关闭本 Issue。

遵照指示暂时关闭:目前已在 `scripts/pack-act-assets.ts` 中通过地面有效掩码(floorMaskedGrid)成功规避了迷宫楼梯和落脚点掉入虚空的问题,满足现阶段连通与可用性诉求;底层全局碰撞网格的空白单元格阻挡策略暂不作进一步调整,先行关闭本 Issue。
Sign in to join this conversation.
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: troytt/diablo2-web#38
No description provided.