164 lines
7.0 KiB
Markdown
164 lines
7.0 KiB
Markdown
# Godot 农场地图拖动功能检验
|
||
|
||
日期:2026-05-12
|
||
|
||
## 范围
|
||
|
||
本轮检验对象是 Godot 主农场的地图拖动预览能力:
|
||
|
||
- 地图可拖动范围最大为 `2000x2000`。
|
||
- 默认进入农场时,土地内容位于地图中心。
|
||
- 背景图不足时先使用现有背景拉伸铺底,不额外补图。
|
||
- 拖动地图不能误触土地菜单、扩建弹窗或长按提示。
|
||
- 缩放、建筑点击、扩建入口和固定 HUD 不能被拖动能力破坏。
|
||
|
||
## 模块拆分结论
|
||
|
||
该功能已经按现有架构拆出,主逻辑没有堆回 `game_scene.gd`。
|
||
|
||
| 模块 | 职责 | 当前判断 |
|
||
| --- | --- | --- |
|
||
| `scripts/ui/farm/farm_viewport_controller.gd` | 农场内容根节点、`2000x2000` 地图、默认居中、单指/鼠标拖动、双指/Ctrl 滚轮缩放、边界 clamp | 拖动功能核心已独立 |
|
||
| `scripts/ui/farm/farm_interaction_coordinator.gd` | 土地点击、长按、菜单、背包;只通过 `on_scene_pan_started/finished` 抑制拖动误触 | 和地图拖动保持低耦合 |
|
||
| `scripts/ui/game_scene_runtime_assembler.gd` | 创建缩放控制器和土地交互模块,并连接拖动开始/结束回调 | 只做装配 |
|
||
| `scripts/ui/game_scene.gd` | 当前只保留扩建图标透明命中区的坐标同步 | 仍有一个可继续迁出的 UI 坐标桥接点 |
|
||
|
||
控制器已命名为 `FarmViewportController`,避免只体现缩放而遗漏拖动视口职责。
|
||
|
||
## 当前实现
|
||
|
||
### 地图边界
|
||
|
||
地图范围使用常量:
|
||
|
||
```gdscript
|
||
const MAP_PREVIEW_SIZE := Vector2(2000.0, 2000.0)
|
||
```
|
||
|
||
拖动偏移由 `_clamped_scene_offset()` 限制:
|
||
|
||
```gdscript
|
||
var scaled_map_size := MAP_PREVIEW_SIZE * _zoom
|
||
var min_offset := _panel_size - scaled_map_size
|
||
clampf(value.x, minf(min_offset.x, 0.0), 0.0)
|
||
clampf(value.y, minf(min_offset.y, 0.0), 0.0)
|
||
```
|
||
|
||
以当前屏幕 `640x960` 为例:
|
||
|
||
| 缩放 | 缩放后地图 | X 偏移范围 | Y 偏移范围 | 结论 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `0.6667` | 约 `1333x1333` | `[-693, 0]` | `[-373, 0]` | 缩小时仍有可预览区域,不露出黑边 |
|
||
| `1.0` | `2000x2000` | `[-1360, 0]` | `[-1040, 0]` | 满足 2000 地图拖动 |
|
||
| `1.3` | `2600x2600` | `[-1960, 0]` | `[-1640, 0]` | 放大后边界随缩放扩大 |
|
||
|
||
边界设计合理:偏移范围以“缩放后的地图尺寸 - 屏幕尺寸”为准,缩放和拖动共用同一套 clamp,不会在缩放后越界。
|
||
|
||
### 默认居中
|
||
|
||
默认进入时执行:
|
||
|
||
1. 把 `Ground`、`House`、`DogHouse`、`FenceBack`、`GameView`、`FenceFront`、`PetLayer` 移到 `FarmSceneRoot`。
|
||
2. 从 `GameView` 下严格匹配 `Land` + 数字的节点计算土地包围盒。
|
||
3. 将土地包围盒中心平移到 `2000x2000` 地图中心。
|
||
4. 将镜头偏移设为地图中心对准屏幕中心。
|
||
|
||
当前顺序是安全的:计算土地包围盒发生在 `LandHitLayer`、`LandLight` 等运行时节点创建前,因此不会把命中层误算进土地范围。
|
||
|
||
### 拖动与点击冲突
|
||
|
||
当前使用 `PAN_DRAG_THRESHOLD = 8` 像素区分点击和拖动。
|
||
|
||
- 移动距离小于阈值:仍按点击处理。
|
||
- 移动距离达到阈值:进入地图拖动。
|
||
- 拖动开始时调用 `FarmInteractionCoordinator.on_scene_pan_started()`。
|
||
- 拖动结束后调用 `on_scene_pan_finished()`,延迟清理抑制标记。
|
||
|
||
土地点击被抑制的路径:
|
||
|
||
```text
|
||
FarmViewportController pan started
|
||
-> FarmInteractionCoordinator.on_scene_pan_started()
|
||
-> stop long press timer
|
||
-> suppress next land press
|
||
-> hide selection UI
|
||
```
|
||
|
||
该设计能避免“拖动土地后弹出土地菜单或扩建弹窗”的误触。
|
||
|
||
### 固定 UI 与农场内容分离
|
||
|
||
只迁移农场内容到 `FarmSceneRoot`,顶部 HUD、左侧按钮、底部世界/家园按钮仍留在主场景固定层。因此拖动地图不会带动 HUD。
|
||
|
||
### 背景策略
|
||
|
||
当前使用 `FarmZoomBackdrop` 将原背景拉伸到 `2000x2000`,并隐藏原 `Ground`。这符合“背景图不够先不用管”的要求。后续补齐大地图背景时,只需要替换 `FarmZoomBackdrop` 的纹理来源,不需要重写拖动逻辑。
|
||
|
||
## 功能检验结果
|
||
|
||
| 检验项 | 结果 |
|
||
| --- | --- |
|
||
| 默认土地在视野中心 | 通过 |
|
||
| 鼠标拖动地图可移动视野 | 通过 |
|
||
| 拖动后不弹扩建面板 | 通过 |
|
||
| 拖动后不弹土地菜单 | 通过 |
|
||
| HUD 不随地图拖动 | 通过 |
|
||
| Web 导出后可运行 | 通过 |
|
||
| smoke 主流程 | 通过 |
|
||
|
||
截图记录:
|
||
|
||
- `docs/ui-baseline/godot-map-pan-final-default.png`
|
||
- `docs/ui-baseline/godot-map-pan-final-after-drag.png`
|
||
- `docs/ui-baseline/godot-map-pan-rename-default.png`
|
||
- `docs/ui-baseline/godot-map-pan-rename-after-drag.png`
|
||
|
||
## 已执行验证
|
||
|
||
```sh
|
||
godot --headless --path godot/FarmGodot --check-only --script res://scripts/ui/farm/farm_viewport_controller.gd
|
||
godot --headless --path godot/FarmGodot --check-only --script res://scripts/ui/farm/farm_interaction_coordinator.gd
|
||
godot --headless --path godot/FarmGodot --check-only --script res://scripts/ui/game_scene_runtime_assembler.gd
|
||
godot --headless --path godot/FarmGodot --check-only --script res://scripts/ui/game_scene.gd
|
||
godot --headless --path godot/FarmGodot --script res://tools/verify_p0.gd
|
||
godot --headless --path godot/FarmGodot --import --quit
|
||
godot --headless --path godot/FarmGodot --export-release Web build/web/index.html
|
||
npm run smoke:godot:web
|
||
```
|
||
|
||
浏览器验证目标:
|
||
|
||
```text
|
||
http://127.0.0.1:8792/index.html
|
||
```
|
||
|
||
## 边界风险
|
||
|
||
| 风险 | 当前影响 | 建议 |
|
||
| --- | --- | --- |
|
||
| `2000x2000` 固定在控制器常量里 | 后续如果不同地图皮肤尺寸不同,需要改代码 | 改成从 `game_scene_runtime_assembler.gd` 的 constants 注入 |
|
||
| 建筑直接命中仍是按点击入口处理 | 拖动从建筑区域开始时,后续可能需要更严格的 tap/release 判定 | 若出现误触,把农场建筑 direct hit 改为 release 后、且未超过拖动阈值才触发 |
|
||
| 缩放没有围绕双指中心或鼠标位置缩放 | 当前可用,但放大时视角体验不够细 | 后续按手势中心调整 `_scene_offset` |
|
||
| 没有惯性滑动和边界回弹 | 当前符合第一版预览需求 | 后续如果要接近原生手感,可加轻量 Tween,但要避免 `_process` 常驻计算 |
|
||
|
||
## 优化优先级
|
||
|
||
P0 暂无阻断问题。
|
||
|
||
P1:
|
||
|
||
- 将地图尺寸从常量改为装配层配置,便于不同背景/地图皮肤切换。
|
||
- 把扩建图标命中区同步从 `game_scene.gd` 迁到 `SceneHitRouter` 或农场交互模块,进一步减薄主场景。
|
||
- 建筑 direct hit 改为 tap 语义,避免未来拖动从房屋/狗窝起手时误触面板。
|
||
|
||
已处理:
|
||
|
||
- `FarmSceneZoomController` 已重命名为 `FarmViewportController`,文件同步改为 `farm_viewport_controller.gd`。
|
||
- 土地包围盒匹配已从 `Land*` 改为 `Land` + 数字,避免 `LandPreview*` 这类装饰节点被纳入默认居中计算。
|
||
|
||
P2:
|
||
|
||
- 增加缩放中心保持逻辑,让双指缩放时手指中点下的地图内容保持稳定。
|
||
- 增加可选惯性拖动和边界阻尼。
|
||
- 背景素材补齐后,把 `FarmZoomBackdrop` 改为真实大图或分块贴图,避免拉伸纹理。
|