[架构重构] 场景系统架构割裂:抽离统一游戏世界领域层,消除 map-scene 沙盒与 act-scene 大地图割裂 #10

Closed
opened 2026-09-14 07:01:32 +00:00 by troytt · 1 comment
Owner

Parent Issue: #7 ([Code Review] 整体代码质量、架构缺陷与技术债务全景审查报告)

模块位置

src/scene/map-scene.ts, src/scene/act-scene.ts, src/game/

问题背景与现象

  • src/scene/map-scene.ts(约 1200 行)拥有完整的单人战役系统(战斗、怪物 AI、攻击/施法、掉落拾取、背包、技能槽、NPC 交谈与存档读档);但它只能在夹具小地图中运行。
  • src/scene/act-scene.ts(约 1125 行)加载了真实的 5 个 Act、35 个正式关卡预解包数据和女法师 DCC/COF,但它仅仅是个漫游器,没有任何战斗、怪物、NPC、背包和交互系统。
  • 两个场景各自手写了一整套相机计算、键盘监听、HUD 文本拼接与状态暴露(window.__d2web*)。

造成的影响

  1. 核心领域模型(World、Entity、Combat、Inventory)没有与视图层(Scene)解耦,代码重复度高。
  2. 用户在真实的 Diablo II 大地图场景中体验不到项目已经开发完备的完整 RPG 核心玩法。

解决建议

  1. 在 src/game/ 中抽取统一的 GameWorld / GameEngine 领域核心状态机;
  2. 将输入管理(sim/input.ts)与相机系统(Camera/Zoom)抽象为可复用组件;
  3. 将 map-scene.ts 中验证成熟的背包、拾取、战斗结算、NPC 对话系统装配进 act-scene.ts;
  4. 使 acts.html 成为真正可交互演进的战役大世界。
> **Parent Issue**: #7 ([Code Review] 整体代码质量、架构缺陷与技术债务全景审查报告) ## 模块位置 `src/scene/map-scene.ts`, `src/scene/act-scene.ts`, `src/game/` ## 问题背景与现象 - `src/scene/map-scene.ts`(约 1200 行)拥有完整的单人战役系统(战斗、怪物 AI、攻击/施法、掉落拾取、背包、技能槽、NPC 交谈与存档读档);但它**只能在夹具小地图中运行**。 - `src/scene/act-scene.ts`(约 1125 行)加载了真实的 5 个 Act、35 个正式关卡预解包数据和女法师 DCC/COF,但它**仅仅是个漫游器**,没有任何战斗、怪物、NPC、背包和交互系统。 - 两个场景各自手写了一整套相机计算、键盘监听、HUD 文本拼接与状态暴露(`window.__d2web*`)。 ## 造成的影响 1. 核心领域模型(`World`、`Entity`、`Combat`、`Inventory`)没有与视图层(Scene)解耦,代码重复度高。 2. 用户在真实的 Diablo II 大地图场景中体验不到项目已经开发完备的完整 RPG 核心玩法。 ## 解决建议 1. 在 `src/game/` 中抽取统一的 `GameWorld` / `GameEngine` 领域核心状态机; 2. 将输入管理(`sim/input.ts`)与相机系统(Camera/Zoom)抽象为可复用组件; 3. 将 `map-scene.ts` 中验证成熟的背包、拾取、战斗结算、NPC 对话系统装配进 `act-scene.ts`; 4. 使 `acts.html` 成为真正可交互演进的战役大世界。
Author
Owner

已修复 — 抽取独立的 GameEngine 领域层 (98d6a6d → 6e6d8ed)

此前完整的玩法逻辑被困在 map-scene.ts 这个 fixture 沙盒里,真实的 5 幕世界 act-scene.ts 只能「漫游」。现已分四步落地,每步单独提交、单独验证:

  1. 抽出 ViewportCamera (src/sim/camera.ts) 与 DEMO_* (src/game/demo-data.ts) —— 纯搬迁,零行为变化。
  2. 新增 src/game/engine.ts,map-scene.ts 退化为薄适配层。引擎内无 DOM / 无 WebGL / 无 window,可在 node 下无头单测。
  3. 通过 WorldMapProvider 端口接入 act-scene.ts(同时提供点查询与范围查询,分别对应两个场景原有的碰撞方式)。
  4. 打开 NPC 接缝,并入地形深度排序渲染队列 —— 为 #6 预留,当时不渲染任何东西。

确定性是硬约束: 引擎内禁用 Math.random() / Date.now() / performance.now(),只用带种子的 Rng,实体 ID 稳定、迭代顺序固定 —— 否则会破坏 src/net/lockstep.ts 的锁步一致性。

遥测契约已保全: window.__d2webMap / window.__d2webAct 的字段在重构前后逐项比对,只增不改不删,scripts/browser/checks/*.js 不受影响。

变异测试(我方独立复现,非采信报告): engine.ts:93 地形守卫 → 触发 "NPCs spawn only on walkable terrain";engine.ts:130 的 > 改 >= → 触发 "Instant spell area effect precisely at boundary"。

### 已修复 — 抽取独立的 `GameEngine` 领域层 (98d6a6d → 6e6d8ed) 此前完整的玩法逻辑被困在 `map-scene.ts` 这个 fixture 沙盒里,真实的 5 幕世界 `act-scene.ts` 只能「漫游」。现已分四步落地,每步单独提交、单独验证: 1. 抽出 `ViewportCamera` (`src/sim/camera.ts`) 与 `DEMO_*` (`src/game/demo-data.ts`) —— 纯搬迁,零行为变化。 2. 新增 `src/game/engine.ts`,`map-scene.ts` 退化为薄适配层。引擎内**无 DOM / 无 WebGL / 无 window**,可在 node 下无头单测。 3. 通过 `WorldMapProvider` 端口接入 `act-scene.ts`(同时提供点查询与范围查询,分别对应两个场景原有的碰撞方式)。 4. 打开 NPC 接缝,并入地形深度排序渲染队列 —— 为 #6 预留,当时不渲染任何东西。 **确定性是硬约束:** 引擎内禁用 `Math.random()` / `Date.now()` / `performance.now()`,只用带种子的 `Rng`,实体 ID 稳定、迭代顺序固定 —— 否则会破坏 `src/net/lockstep.ts` 的锁步一致性。 **遥测契约已保全:** `window.__d2webMap` / `window.__d2webAct` 的字段在重构前后逐项比对,只增不改不删,`scripts/browser/checks/*.js` 不受影响。 **变异测试(我方独立复现,非采信报告):** `engine.ts:93` 地形守卫 → 触发 *"NPCs spawn only on walkable terrain"*;`engine.ts:130` 的 `>` 改 `>=` → 触发 *"Instant spell area effect precisely at boundary"*。
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.

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