fix(automap): 小地图(Automap/Minimap)没有随着大地图区域切换而同步切换 #549

Closed
opened 2026-10-02 07:25:05 +00:00 by troytt · 1 comment
Owner

问题描述

当玩家在同一幕(Act)内跨区域切换地图时(例如从 areaId=1 罗格营地走到 areaId=2 鲜血荒地、进入 areaId=8 邪恶洞穴等地下城入口、使用传送门 Town Portal 或同幕传送站 Waypoint),主视角大地图(ClientWorldSceneSource / LevelView)已根据玩家世界坐标实时切换到新区域,但小地图(AutomapView / Minimap)仍然停留在初始 0x03 LoadAct 所在的旧区域,导致小地图名称、地形层、碰撞网格与玩家当前所在大地图不一致。

1.13c Ground Truth (D2Client.dll)

  • 在 1.13c 在线协议中,0x03 LoadAct (0x6FB5DB50) 仅在进入游戏或跨幕时发送;同幕跨区域移动/传送仅下发 0x07 AddRoomData (RoomReveal) 与玩家位置更新(0x15 / 0x95 / 移动包)。
  • 1.13c AUTOMAP_UpdateVisibility (0x6FB10CF0) 每帧根据玩家当前所在 Room 所属的 pLevel->dwLevelNo 判断区域切换,一旦 dwLevelNo 变化即调用 InitAutomapLayer(dwLevelNo) 切换小地图层级并保留同幕已探索 Room 记录。

根因分析

  1. OnlineSession.handleServerEvent 仅在 LoadAct 时调用 this.automapView.setContext(...),且 ClientWorld.apply 仅在 this.areaId === 0 时从 RoomReveal 初始化 areaId,导致同幕切换区域时 automapView.currentAreaId 与 world.areaId 停留在旧区域。
  2. renderSceneSourceFrame (src/client/scene/world-renderer.ts) 在构建 minimapLevel 时未将 ctx.automapView 同步到当前激活的 levelView,直接取旧区域的 buildMinimapLevel() 数据并覆盖了新区域的 isoOriginX/Y,造成坐标系错位。
  3. Minimap.render (src/client/ui/minimap.ts) 在 level.key 切换时未重置手动平移偏移量(panX/panY)。

修复方案

  1. 在 AutomapView 中新增 syncActiveLevel(areaId, levelView),在同幕切换区域时更新 currentAreaId 与缓存的 LevelView,同时保留当前 (mapSeed, difficulty) 下各区域已探索的 Room 状态。
  2. 在 OnlineSession 中根据玩家实时解析出的 levelView.areaId 同步更新 world.areaId、automapView.syncActiveLevel、soundService.onEnterArea 与 hudModel。
  3. 在 world-renderer.ts 的 renderSceneSourceFrame 中同步 ctx.automapView 并传入当前 levelView.areaId 构建小地图数据。
  4. 在 Minimap.render 中检测到 level.key 变化时自动调用 resetPan()。
## 问题描述 当玩家在同一幕(Act)内跨区域切换地图时(例如从 `areaId=1` 罗格营地走到 `areaId=2` 鲜血荒地、进入 `areaId=8` 邪恶洞穴等地下城入口、使用传送门 Town Portal 或同幕传送站 Waypoint),主视角大地图(`ClientWorldSceneSource` / `LevelView`)已根据玩家世界坐标实时切换到新区域,但小地图(`AutomapView` / `Minimap`)仍然停留在初始 `0x03 LoadAct` 所在的旧区域,导致小地图名称、地形层、碰撞网格与玩家当前所在大地图不一致。 ## 1.13c Ground Truth (`D2Client.dll`) - 在 1.13c 在线协议中,`0x03 LoadAct` (`0x6FB5DB50`) 仅在进入游戏或跨幕时发送;同幕跨区域移动/传送仅下发 `0x07 AddRoomData` (`RoomReveal`) 与玩家位置更新(`0x15` / `0x95` / 移动包)。 - 1.13c `AUTOMAP_UpdateVisibility` (`0x6FB10CF0`) 每帧根据玩家当前所在 Room 所属的 `pLevel->dwLevelNo` 判断区域切换,一旦 `dwLevelNo` 变化即调用 `InitAutomapLayer(dwLevelNo)` 切换小地图层级并保留同幕已探索 Room 记录。 ## 根因分析 1. `OnlineSession.handleServerEvent` 仅在 `LoadAct` 时调用 `this.automapView.setContext(...)`,且 `ClientWorld.apply` 仅在 `this.areaId === 0` 时从 `RoomReveal` 初始化 `areaId`,导致同幕切换区域时 `automapView.currentAreaId` 与 `world.areaId` 停留在旧区域。 2. `renderSceneSourceFrame` (`src/client/scene/world-renderer.ts`) 在构建 `minimapLevel` 时未将 `ctx.automapView` 同步到当前激活的 `levelView`,直接取旧区域的 `buildMinimapLevel()` 数据并覆盖了新区域的 `isoOriginX/Y`,造成坐标系错位。 3. `Minimap.render` (`src/client/ui/minimap.ts`) 在 `level.key` 切换时未重置手动平移偏移量(`panX`/`panY`)。 ## 修复方案 1. 在 `AutomapView` 中新增 `syncActiveLevel(areaId, levelView)`,在同幕切换区域时更新 `currentAreaId` 与缓存的 `LevelView`,同时保留当前 `(mapSeed, difficulty)` 下各区域已探索的 Room 状态。 2. 在 `OnlineSession` 中根据玩家实时解析出的 `levelView.areaId` 同步更新 `world.areaId`、`automapView.syncActiveLevel`、`soundService.onEnterArea` 与 `hudModel`。 3. 在 `world-renderer.ts` 的 `renderSceneSourceFrame` 中同步 `ctx.automapView` 并传入当前 `levelView.areaId` 构建小地图数据。 4. 在 `Minimap.render` 中检测到 `level.key` 变化时自动调用 `resetPan()`。
Author
Owner

已在 commit 102d001(合并至 main 47d18fc)完成修复并合入主干:

修复内容

  1. AutomapView.syncActiveLevel (src/client/automap/automap-view.ts):
    • 新增 syncActiveLevel(areaId, existingLevelView),在同一局游戏/同一 mapSeed 内切换区域时同步更新 currentAreaId 与 currentActId 并缓存 LevelView,同时保留已探索的 serverRevealedRooms(0x07)与跨区域缓存。
    • buildMinimapLevel(lang, areaId?) 支持直接接收目标 areaId,直接使用 LevelView 的真实网格尺寸(cellsX/cellsY/blocked/isoOriginX/isoOriginY),并合并 view.entrances 的跨区域出口标记。
  2. OnlineSession.syncActiveAreaFromPlayerPosition (src/client/session/online-session.ts):
    • 维护 knownAreaIds(记录 LoadAct 初始区域与所有 0x07 RoomReveal 区域),在 handleServerEvent、tick 与 sceneSource 中基于玩家当前世界子瓦片坐标动态解析所在 LevelView。
    • 当玩家跨出室外边界、进入地牢入口(Warp)、使用传送门或小站进入新区域时,无需等待新的 0x03 LoadAct,立即同步更新 world.areaId、automapView.syncActiveLevel、soundService.onEnterArea 与 hudModel.syncFromWorld。
  3. renderSceneSourceFrame & Minimap (src/client/scene/world-renderer.ts, src/client/ui/minimap.ts):
    • 渲染帧构建小地图前强制将 ctx.automapView 与当前大地图 levelView / snapshot.levelId 对齐。
    • Minimap.draw 在 level.key 切换时自动调用 resetPan() 重置手动平移偏移。
  4. CommandMapper.startLocalPathPrediction (src/client/input/command-mapper.ts):
    • 优先通过 mapService.findLevelViewAtWorldSubTile 解析玩家当前所在区域的 LevelView,避免邻近区域 0x07 预揭示覆盖 getActiveLevelView() 导致本地寻路越界。
  5. 回归测试 (tests/client/minimap-level-switch-549.test.ts):
    • 覆盖 Area 1 -> Area 2 -> Area 8 -> Area 1 无 LoadAct 连续区域切换、renderSceneSourceFrame + Minimap 渲染管线同步与手动平移重置、以及 CommandMapper 邻近区域预揭示下的本地寻路。
已在 commit `102d001`(合并至 `main` `47d18fc`)完成修复并合入主干: ### 修复内容 1. **`AutomapView.syncActiveLevel` (`src/client/automap/automap-view.ts`)**: - 新增 `syncActiveLevel(areaId, existingLevelView)`,在同一局游戏/同一 `mapSeed` 内切换区域时同步更新 `currentAreaId` 与 `currentActId` 并缓存 `LevelView`,同时保留已探索的 `serverRevealedRooms`(`0x07`)与跨区域缓存。 - `buildMinimapLevel(lang, areaId?)` 支持直接接收目标 `areaId`,直接使用 `LevelView` 的真实网格尺寸(`cellsX`/`cellsY`/`blocked`/`isoOriginX`/`isoOriginY`),并合并 `view.entrances` 的跨区域出口标记。 2. **`OnlineSession.syncActiveAreaFromPlayerPosition` (`src/client/session/online-session.ts`)**: - 维护 `knownAreaIds`(记录 `LoadAct` 初始区域与所有 `0x07 RoomReveal` 区域),在 `handleServerEvent`、`tick` 与 `sceneSource` 中基于玩家当前世界子瓦片坐标动态解析所在 `LevelView`。 - 当玩家跨出室外边界、进入地牢入口(Warp)、使用传送门或小站进入新区域时,无需等待新的 `0x03 LoadAct`,立即同步更新 `world.areaId`、`automapView.syncActiveLevel`、`soundService.onEnterArea` 与 `hudModel.syncFromWorld`。 3. **`renderSceneSourceFrame` & `Minimap` (`src/client/scene/world-renderer.ts`, `src/client/ui/minimap.ts`)**: - 渲染帧构建小地图前强制将 `ctx.automapView` 与当前大地图 `levelView` / `snapshot.levelId` 对齐。 - `Minimap.draw` 在 `level.key` 切换时自动调用 `resetPan()` 重置手动平移偏移。 4. **`CommandMapper.startLocalPathPrediction` (`src/client/input/command-mapper.ts`)**: - 优先通过 `mapService.findLevelViewAtWorldSubTile` 解析玩家当前所在区域的 `LevelView`,避免邻近区域 `0x07` 预揭示覆盖 `getActiveLevelView()` 导致本地寻路越界。 5. **回归测试 (`tests/client/minimap-level-switch-549.test.ts`)**: - 覆盖 `Area 1 -> Area 2 -> Area 8 -> Area 1` 无 `LoadAct` 连续区域切换、`renderSceneSourceFrame` + `Minimap` 渲染管线同步与手动平移重置、以及 `CommandMapper` 邻近区域预揭示下的本地寻路。
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#549
No description provided.