Files
mtgodot-poc/audit/contracts/ui.input-camera-mobile.md
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

130 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ui.input_camera_mobile —— 鼠标、相机、光标、IME、触屏和移动端窗口
状态:`PARTIAL`
本合同检查 40250 的输入优先级、键盘状态、鼠标按钮映射、智能拾取、相机连续控制、事件相机、光标状态、IME 生命周期,以及当前端的触屏/移动端适配。判断标准是状态机、调用顺序、拒绝条件、清理副作用和可观察结果一致;“有一个可用的 Godot 控件”不能单独视为与 40250 等价。
## 40250 参考调用链
- `UserInterface/PythonPlayerInputKeyboard.cpp`
- `NEW_SetSingleDIKKeyState` 先取消钓鱼,再处理方向键;`NEW_SetSingleDirKeyState` 更新方向按键状态并立即进入多方向计算。
- `NEW_SetMultiDirKeyState``__CanMove` 允许时按 WASD/方向键组合计算唯一旋转角,支持精确的四方向和四个对角方向;无方向时调用 `NEW_Stop`
- 攻击键在钓鱼状态下走钓鱼分支,否则更新攻击状态。
- `UserInterface/PythonPlayerInputMouse.cpp`
- 移动、智能、相机、自动攻击、普通攻击和技能按钮由统一的 `NEW_SetMouseState` 分派。
- 智能点击优先级为物品→角色→地面,并受私店、表情、眩晕等状态门控;相机按下进入拖拽,点击结束拖拽或选中目标并打开角色菜单。
- 中键相机、Ctrl 行为、技能按钮和拾取/地面射线均有独立状态清理。
- `UserInterface/PythonApplicationCamera.cpp`
- 普通、站立、混合三类相机状态按帧更新;事件相机保存默认相机,混合期间锁定直接控制,结束后恢复普通相机。
- 相机旋转、俯仰和缩放使用连续速度;缩放按 `1.0 + zoomSpeed * direction` 的比例更新,而不是固定距离步进。
- `UserInterface/PythonApplicationCursor.cpp` / `EterLib/Input.cpp`
- 光标有连续光标状态、硬件光标显隐和相机旋转等光标形状;DirectInput 键盘维护 256 字节状态并支持重新获取。
- `UserInterface/PythonIME.cpp`
- 文本输入显式处理左右/Home/End/删除、Tab/Return/Escape、字符输入、候选窗口和输入法状态回调。
## 当前端实现映射
- `project/player_controller.gd` / `project/game_scene.gd`
- 已实现桌面 WASD/方向键状态、按键释放清理、地面/物品/角色点击优先级,以及触屏点击与拖拽分离;移动端摇杆复用同一移动入口。
- 当前按键分为 `_input` 处理释放、`_unhandled_input` 处理按下,和 40250 的统一即时输入回调不是同一时序;角色碰撞/拾取也由 Godot 射线和简化碰撞半径适配。
- `project/game_camera.gd`
- 已有桌面右键拖拽、滚轮缩放、触屏单指旋转、双指缩放、Q/E/R/F/T/G 连续控制、普通/站立/混合事件相机、地形遮挡和镜头震动。
- 当前默认距离/俯仰角、固定缩放步进、相机目标高度和事件相机锁定/恢复语义没有与 `CCamera` 的常量和比例缩放逐项统一。
- `project/ui/mouse_controller.gd` / `project/ui/cursor_manager.gd`
- 已实现物品/技能/表情拖拽、右键/ESC/失焦取消,以及 `.sub` 光标资源加载和 Godot 光标映射。
- 没有完整复刻 `NEW_SetMouseState` 的所有按钮分派;没有显式连续光标编号状态、硬件光标显隐封装和相机拖拽时隐藏硬件光标的等价链路。
- `project/ui/mobile/*`
- 已有安全区、横屏布局、虚拟摇杆、触屏按钮、长按技能瞄准、菜单返回和窗口焦点/暂停时触摸清理;这是移动平台适配层,不是 40250 Windows 输入实现本身。
- Godot `LineEdit`/聊天 UI
- 当前依赖引擎默认文本输入,没有独立的 `PythonIME` 等价封装来记录光标移动、删除、候选窗口、编码页和输入法回调。
## 分支等价矩阵
| 40250 判据/副作用 | 当前实现 | 结论 |
|---|---|---|
| WASD/方向键组合、对角角度、无键停止和按键释放清理 | `_wasd` 优先级、归一化、角色朝向和焦点清理已实现,输入按下/释放分属不同 Godot 阶段 | 部分等价;基础行为通过,时序和全部 `__CanMove`/钓鱼门控未形成同链证据 |
| 鼠标 MOVE/SMART/CAMERA/AUTO/ATTACK/SKILL 统一分派 | 物品、角色、地面点击和相机拖拽分别分布在 PlayerController/GameCamera/Quickbar | 部分等价;完整按钮映射、Ctrl/中键/角色菜单和拒绝分支未统一证明 |
| 智能点击物品→角色→地面及状态门控 | 当前 ground item→实体→地面顺序存在,触摸仲裁也存在 | 部分等价;私店、表情、眩晕、可拾取资格和 native 射线语义仍为适配近似 |
| 普通相机按帧跟随、旋转/俯仰/比例缩放 | 当前有跟随、拖拽、滚轮和连续键控制 | 不等价;缩放为固定距离步进,默认值/目标高度/连续速度尚未与参考公式统一;`game_camera_test` 3 项失败 |
| 事件相机保存、混合、锁定和恢复 | 有 NORMAL/STAND/BLEND 与字典插值/恢复 | 部分等价;缺少完整 CameraManager 状态锁、roll/视线/交叉线和异常中断清理证据 |
| 连续光标、相机旋转光标、硬件光标显隐 | 有资源加载和形状映射 | 部分等价;没有连续光标状态,拖拽期间未证明隐藏硬件光标,运行时自定义光标形状映射也不完整 |
| IME 光标/删除/候选/字符回调 | 依赖 Godot LineEdit | GAP;没有可审计的独立等价实现和回调测试 |
| 触屏、摇杆、双指缩放、移动窗口和安全区 | mobile UI/gesture/window 测试通过 | 平台适配通过;不应误标为 Windows 40250 输入链等价 |
| 失焦、暂停、重连、退出时输入状态清理 | PlayerController、MouseController、MobileOverlay 分别清理 | 部分等价;缺少 GameScene 统一清理和相机/鼠标/IME 组合状态的生产级测试 |
## 已执行测试
- `project/mouse_controller_test.gd`:通过;覆盖全局物品拖拽状态和取消。
- `project/input_key_test.gd`:通过;覆盖 ClientVS22 桌面按键映射、Alt、快捷栏和模态 UI;进程有 20 个 ObjectDB 和 1 个 Camera RID 泄漏警告。
- `project/mobile_gesture_test.gd`:通过;覆盖长按、技能瞄准、快捷栏切换和物品触摸拖拽。
- `project/mobile_ui_test.gd`:通过;覆盖平台配置、横屏 HUD、菜单和控件。
- `project/mobile_window_host_test.gd`:通过;覆盖安全区、触摸桥和模态返回。
- `project/game_camera_test.gd`:失败 3 项:期望的默认距离 1550cm、默认俯仰角 27° 和 `RESTORE_CAMERA` 恢复保存普通视图均未满足;因此相机不能标记为等价。
上述三项是修复前的基线失败,已在本轮修正;后续结果见本合同末尾的实现修复记录。
## Deep audit round 2026-09-21
### Fresh regression results (修复前基线)
- `mouse_controller_test.gd``mobile_gesture_test.gd``mobile_ui_test.gd``mobile_window_host_test.gd` 均退出码 0;覆盖拖放取消、长按/技能瞄准、横屏控件、安全区、触摸桥和模态返回。
- `input_key_test.gd` 断言通过,但有 20 个 ObjectDB 和 1 个 Camera RID 泄漏。
- `game_camera_test.gd` 仍失败 3 项:默认距离 1550cm、默认俯仰 27°、`RESTORE_CAMERA` 恢复保存普通视图。
### Static parity conclusion (修复前基线)
- WASD/触屏和基础鼠标拖放路径可运行,但输入按下/释放分散在不同 Godot 阶段,尚未形成 40250 `NEW_SetSingleDIKKeyState`/统一鼠标状态分派的同一时序。
- 相机缩放、默认常量和恢复快照仍与参考不一致;事件相机的异常中断、相机/鼠标/IME 组合清理没有生产级证据。
- IME 仍依赖 Godot LineEdit,没有可审计的候选窗口、字符/删除/Home/End/Return/Escape 回调链;移动端测试通过不等于 Windows 40250 输入链一致。
修复前本轮仍为 `PARTIAL`;随后已针对相机默认值、缩放和目标高度完成实现修复,整体合同仍保持 `PARTIAL`
### Active EterLib core review
- `EterLib/Camera.cpp``CCamera` 维护普通/事件相机状态、Begin/EndDrag/Drag、滚轮和按比例 `Zoom(float ratio)`;相机管理器还负责当前相机、事件相机和恢复路径。当前 `game_camera.gd` 虽有同名交互入口,但使用固定距离步进,默认距离/俯仰和恢复快照已有 3 项测试失败,不能视为公式或状态机一致。
- `EterLib/IME.cpp` 不只是文本框包装:它维护输入法启停、键盘捕获、组合字符串、候选/reading 窗口、code page、TSF 回调和字符串编辑。当前依赖 Godot `LineEdit`,没有对应的候选窗口、组合提交、编码页和回调生命周期;这是 Windows 输入链的明确缺口。
- 因此本轮确认 `Camera.cpp/.h``IME.cpp/.h``Input.h``msctf.h` 已完成活跃源码静态核对,但实现仍为 `PARTIAL`,静态核对不等于运行时一致。
## 已确认差异和未验证项
1. 已统一参考端与当前端的相机默认常量、单位、比例缩放、滚轮阻尼、连续控制速度、相机目标高度和恢复快照;`game_camera_test.gd` 的修复前 3 项失败已通过。
2. 建立鼠标按钮分派矩阵,覆盖 MOVE/SMART/CAMERA/AUTO/ATTACK/SKILL、Ctrl、中键、技能/物品/角色/地面优先级和每个拒绝分支。
3. 增加硬件光标显隐、连续光标、相机拖拽结束、失焦、暂停和异常退出的组合测试。
4. 为聊天/输入框补充 IME 适配合同:光标移动、Home/End、删除、Return/Escape、候选窗口和字符回调;不能以 LineEdit 存在代替功能等价。
5. 增加生产 GameScene 级输入清理测试,确认切图、断线、重连、窗口失焦和移动端触摸取消后没有粘滞按键、相机拖拽或拖放状态。
## 下一轮验证入口
- 先定位 `game_camera.gd` 默认值和 `RESTORE_CAMERA` 失败的具体断言,按 40250 的 Camera 状态快照建立边界测试。
- 再审计 `audio.bgm_ambience_3d`,核对 BGM、区域环境音、角色音效、3D 监听器和前后台恢复调用链。
## Active UserInterface IME bridge review
- `PythonIME.h`/`PythonIMEModule.cpp` 是 Windows IME 的完整桥:光标左右/Home/End/删除/设置位置、Create/Destroy、Tab/Return/Escape、`WM_CHAR`、候选/reading 更新、codepage 和输入模式都通过 `CPythonIME`/`EterLib::IME` 暴露给 Python。
- 当前端 `LineEdit` 能完成基本 UTF-8 文本输入,但没有证明多字节 codepage、候选窗口、reading string、组合态删除、焦点切换和系统 IME 回调的状态顺序一致;因此不能把普通键盘 smoke test 视为 IME 等价。
## Static review evidence
已将 `PythonIME.h``PythonIMEModule.cpp` 纳入 active source 证据;相机/输入合同仍为 `PARTIAL`,需补真实输入法和失焦/取消清理测试。
## Implementation fix round 2026-09-21
### Reference chain
- `source/UserInterface/CameraProcedure.cpp::CCamera::Update`:距离边界为 200/2500cm,滚轮速度按帧应用并以 0.5 阻尼,目标高度按距离从 100cm 插值到 150cm。
- `source/UserInterface/PythonApplication.cpp`:默认相机缩放速度为 `0.05`
- `source/UserInterface/PythonApplicationCamera.cpp``ZoomCamera` 使用 `1.0 + speed * direction` 的比例缩放。
- `source/EterLib/Camera.cpp::CCamera::Wheel`Win32 滚轮量按 0.3 resistance 转换为距离速度。
### Current implementation
- `project/game_camera.gd` 已将默认值固定为 1550cm/27°,加入 200/2500cm 对应的目标高度插值、0.05 比例键盘缩放、36cm/刻度的滚轮速度和 0.5 阻尼;事件相机锁定时不消费普通缩放输入。
- `project/game_camera_test.gd` 已覆盖默认值、`RESTORE_CAMERA`、目标高度的最小/最大距离边界、滚轮缩放阻尼和键盘 0.05 比例。
### Verification
- `game_camera_test.gd`:通过;默认值、事件相机恢复、动态目标高度、滚轮阻尼和比例缩放均通过。
- `input_key_test.gd``mobile_gesture_test.gd``mobile_ui_test.gd``mobile_window_host_test.gd`:通过;`input_key_test.gd` 仍报告既有的 20 个 ObjectDB 与 1 个 Camera RID 泄漏警告。
该修复只闭合了相机默认值和缩放/目标高度的已确认差异;IME、完整鼠标按钮分派、硬件/连续光标和异常清理仍保持 `PARTIAL`,不能据此宣称输入系统整体等价。