Files
mtgodot-poc/docs/MOBILE-UI-IMPLEMENTATION.md

657 lines
30 KiB
Markdown
Raw Permalink 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 与现有 PC UI 共存实施方案
## 1. 目标与结论
本方案用于把横屏移动端 UI 草图落地到现有 Godot 客户端,同时保留当前 PC UI、40250 交互语义、网络协议和玩法逻辑。
草图入口:[`../.planning/sketches/001-mobile-hud/index.html`](../.planning/sketches/001-mobile-hud/index.html)
总体结论:
- 不重写网络、角色、物品、技能、任务、组队和商店数据逻辑。
- PC 与移动端共用同一套客户端状态和业务控制器,分别使用不同的 HUD 与窗口布局。
- 首页 HUD、虚拟摇杆、技能盘、菜单路由和移动端输入仲裁需要新增。
- 高频窗口需要移动端专用布局;低频窗口可先套移动端容器,随后逐步重排。
- 服务端或 NPC 上下文驱动的窗口不能被菜单强制打开,菜单只能展示入口条件或导航到对应场景。
- 整体属于 UI 层中等偏大改动,但不是客户端重写。预计可复用约 70% 的现有功能逻辑。
## 2. 已确定的移动端设计规则
### 2.1 设备与视觉方向
- 横屏为唯一移动端布局方向。
- 设计基准为 844×390,布局必须适配更宽的 Android 屏幕、平板和刘海/挖孔安全区。
- 风格采用现代手游半透明 HUD、暗色玻璃、暖铜金边框和低饱和背景。
- 不直接复制其他游戏素材,只参考暗黑 ARPG 的信息层级、触控半径和功能收纳方式。
### 2.2 首页 HUD
```text
┌──────────────────────────────────────────────────────────────┐
│ 名称 背包 小地图 菜单 │
│ 头像 │
│ HP/MP 目标名称与血条 │
│ 队员 1 │
│ 队员 2 当前任务追踪 │
│ │
│ 游戏世界 │
│ │
│ 移动摇杆 技能 / 普攻 │
└──────────────────────────────────────────────────────────────┘
```
固定规则:
- 玩家名称位于左上头像上方。
- HP、MP 和职业资源条位于头像下方,宽度与头像一致。
- 点击玩家头像进入角色、装备、技能、坐骑、表情和称号页面。
- 角色功能不出现在右上菜单。
- 组队后,队员头像、名称、HP 和距离显示在玩家头像下方;未组队时不占空间。
- 队伍不出现在社交菜单,也不设置独立按钮。
- 右上角从左到右为背包、小地图、菜单;点击小地图打开大地图。
- 当前任务条直接打开任务日志,不放入菜单。
- 底部中央不设置功能栏,只在左下和右下保留必要战斗控件。
### 2.3 右上菜单
菜单只保留五个一级分类:
| 一级分类 | 二级内容 |
|---|---|
| 社交 | 聊天、好友、公会、情侣、观战 |
| 成长 | 精炼、龙魂、Cube 制作 |
| 活动 | 副本、钓鱼 |
| 商业 | 商店、商城、交易、私人商店、仓库、兑换 |
| 系统 | 系统设置、游戏设置、帮助、选择角色、登出、退出 |
页面层级最多两层。二级页必须提供返回上一级和关闭两个操作。
## 3. 当前代码基础
### 3.1 UI 装配
`project/game_scene.gd` 已负责创建并连接以下模块:
- `Hud`
- `InventoryUI`
- `SkillUI`
- `CharStatusUI`
- `Quickbar`
- `QuestLog`
- `PartyUI`
- `FriendUI`
- `GuildUI`
- `LoveUI`
- `ShopUI`
- `ExchangeUI`
- `SafeboxUI`
- `MallUI`
- `CubeUI`
- `RefineUI`
- `DragonSoulUI`(当前功能开关关闭)
- `Minimap`
- `AtlasUI`
- `SystemMenuUI`
这些模块已经连接 `M2Client` 信号并负责现有功能行为。移动端不应复制这一套网络和状态处理。
### 3.2 现有 HUD 接口可作为兼容协议
`project/net_play.gd` 通过以下方法更新 `Hud`
```gdscript
set_vitals(hp, max_hp, sp, max_sp)
set_stamina(value, max_value)
set_exp(xp, next_xp)
set_level(level)
set_energy(value, max_value)
set_target(name, hp_pct)
clear_target()
```
移动端 HUD 实现相同的方法后,可以直接作为 `net_play.setup(..., hud_node)` 的 HUD 参数,无需修改网络更新流程。
### 3.3 PC 固定布局不能直接缩放成移动端
当前工程基准分辨率为 1920×1080,`UiManager.screen` 也默认使用该尺寸。`hud.gd` 中状态条锚定左下且 HP 宽度为 240,现有 Python UI 脚本还包含大量固定坐标。
因此不能把整个 PC CanvasLayer 等比缩小后直接作为最终移动端 UI,否则会出现:
- 字体和触控目标过小。
- 横屏短边空间不足。
- 多窗口互相覆盖。
- 标题栏拖动、右键、鼠标悬停等 PC 交互无法在触屏完成。
- 刘海、圆角和系统手势区遮挡 UI。
### 3.4 当前触摸能力
现有代码已经具备:
- 单指轻点地面移动或选择实体。
- 单指拖动旋转镜头。
- 双指缩放镜头。
- `pointing/emulate_mouse_from_touch=false`,不会把触摸再次模拟成鼠标事件。
当前已具备:
- 虚拟摇杆连续移动,以及普攻、药水和 4 个技能触摸按钮。
- UI 触点按 index 捕获,摇杆/战斗触点不会被世界点地和镜头误判;窗口打开时由 `UiManager` 阻断世界输入。
- 双指世界手势不会生成点地移动;切后台、失焦和恢复时会释放摇杆/攻击状态。
- 背包支持点按选择、再次点按使用、点另一格移动;交易、私人商店、仓库和商城支持移动端候选列表或点按操作。
代码已覆盖:
- 技能长按瞄准、拖动调整方向和松手释放;短按仍沿用现有目标/自动射程逻辑。
- 角色属性/装备、技能、聊天/私聊、系统设置和游戏设置的移动横屏页面。
- 物品长按详情与触摸拖放、技能拖入快捷栏、快捷栏槽位交换;无法拖放时仍保留点按替代路径。
后续主要是 Android/iOS 真机上的 DPI、键盘、手势区和视觉验收。
### 3.5 现有队伍 UI 可直接演进
`PartyUI` 已经:
- 监听 `party_changed` 和生命状态更新。
- 队伍为空时自动隐藏。
- 显示成员名字、HP、队长和角色状态。
- 点击成员可选中对应角色。
- 支持角色分配、踢人、经验分配和组队治疗。
移动端只需要增加紧凑布局模式并更换挂载位置,无需重写队伍逻辑。
## 4. 目标架构
```text
GameScene
├── M2Client / NetPlay / PlayerController / GameCamera
│ └── PC 与移动端共享,不复制协议和玩法状态
├── UiManager
│ └── 继续管理功能窗口栈、模态遮罩和关闭顺序
├── DesktopUiRoot
│ ├── 当前 Hud
│ ├── 当前 Quickbar
│ └── 当前 PC 窗口布局
└── MobileUiRoot
├── MobileHud
│ ├── PlayerCard
│ ├── PartyHost
│ ├── TargetCard
│ ├── QuestTracker
│ ├── MinimapHost
│ └── TopRightActions
├── MobileInputOverlay
│ ├── VirtualJoystick
│ ├── AttackButton
│ ├── SkillWheel
│ └── ContextAction
├── MobileMenuDrawer
├── MobileWindowHost
└── MobileUiRoot(当前同时承担路由职责)
```
职责边界:
- `MobileHud` 只显示状态并发出点击信号,不直接发送网络包。
- `MobileInputOverlay` 把触控动作转换为现有控制器调用。
- `MobileUiRoot` 把头像、背包、小地图、任务和菜单项路由到现有 UI 控制器;后续如路由继续膨胀,再拆出独立 `MobileUiRouter`
- `MobileWindowHost` 负责安全区、全屏面板、返回键和过渡动画。
- 原有 UI 控制器继续处理数据、服务器信号和业务动作。
## 5. UI 模式选择
新增 `UiProfile`,支持 `AUTO``DESKTOP``MOBILE` 三种模式。
自动选择建议:
```gdscript
enum Mode { AUTO, DESKTOP, MOBILE }
static func resolve(mode: Mode) -> Mode:
if mode != Mode.AUTO:
return mode
return Mode.MOBILE if OS.has_feature("mobile") else Mode.DESKTOP
```
要求:
- Android 和 iOS 默认移动 UI。
- macOS 默认 PC UI。
- 桌面开发时允许通过项目设置或启动参数强制移动 UI,便于调试。
- 自动判断只在进入游戏时执行,切换 UI 模式需要重建 UI 根节点,避免运行中残留窗口。
建议新增项目配置:
```text
mt/ui/profile = auto | desktop | mobile
mt/ui/mobile_scale = 1.0
mt/ui/show_touch_debug = false
```
## 6. 建议文件结构
```text
project/ui/
├── mobile/
│ ├── mobile_ui_root.gd
│ ├── mobile_hud.gd
│ ├── mobile_input_overlay.gd
│ ├── mobile_menu_drawer.gd
│ ├── mobile_ui_router.gd
│ ├── mobile_window_host.gd
│ └── components/
│ ├── player_card.gd
│ ├── party_member_strip.gd
│ ├── quest_tracker.gd
│ ├── virtual_joystick.gd
│ ├── skill_button.gd
│ └── item_touch_slot.gd
├── desktop/
│ └── 可后续把当前 HUD 迁入,首期不要求移动文件
└── ui_profile.gd
```
首期不要搬动所有现有文件,以减少路径变化和回归风险。
## 7. 现有功能与移动端入口映射
### 7.1 首页直达入口
| 移动端入口 | 现有目标 | 接入方式 | 改动等级 |
|---|---|---|---|
| 玩家头像 | `CharStatusUI``SkillUI` | 头像打开角色容器;容器内调用现有角色/技能状态 | 中 |
| 背包 | `InventoryUI.toggle()` | 右上按钮经路由器调用 | 低 |
| 小地图 | `AtlasUI.toggle()` | 小地图自身保持显示,点击打开大地图 | 低 |
| 当前任务 | `QuestLog.toggle()` | 任务条点击调用;不进入菜单 | 低 |
| 队员条 | `PartyUI` | 增加 compact 模式,挂载到玩家头像下 | 中低 |
| 普攻 | `NetPlay.set_attack_key()` | 按下设为 true,抬起设为 false | 中 |
| 技能 | `Quickbar.activate(slot)` | 技能按钮使用现有槽位和冷却逻辑 | 中 |
| 拾取/交互 | `GroundItems``NetPlay` | 使用现有拾取、实体选择和场景交互路径 | 中 |
### 7.2 右上菜单路由
| 分类 | 功能 | 当前可否直接打开 | 移动端行为 |
|---|---|---|---|
| 社交 | 聊天 | 可以 | 调用现有聊天日志入口 |
| 社交 | 好友 | 可以 | 调用 `FriendUI.toggle()` |
| 社交 | 公会 | 可以 | 调用 `GuildUI.toggle()` |
| 社交 | 情侣 | 状态驱动 | 展示情侣状态;无关系时显示空状态 |
| 社交 | 观战 | 场景状态驱动 | 展示观战说明或当前观战状态 |
| 成长 | 精炼 | 服务器询问驱动 | 显示“前往精炼 NPC”,收到 `refine_ask` 后打开窗口 |
| 成长 | 龙魂 | 当前禁用 | 在 `ENABLE_DRAGON_SOUL` 开启前隐藏或显示未开放 |
| 成长 | Cube 制作 | NPC/服务器驱动 | 导航到制作 NPC;收到 `cube_opened` 后打开 |
| 活动 | 副本 | 任务/场景驱动 | 展示目标与导航,不绕过服务端进入条件 |
| 活动 | 钓鱼 | 场景驱动 | 展示说明;到达钓点后由情境按钮开始 |
| 商业 | NPC 商店 | NPC 驱动 | 导航或提示靠近 NPC,不能本地强制打开 |
| 商业 | 商城 | 服务器状态驱动 | 有商城会话时显示内容,否则显示不可用状态 |
| 商业 | 玩家交易 | 玩家交互驱动 | 选择附近玩家后发起交易 |
| 商业 | 私人商店 | 玩家/摊位驱动 | 点击摊位浏览;自己的商店进入管理模式 |
| 商业 | 仓库 | NPC/服务器驱动 | 导航到仓库 NPC,服务器打开后显示 |
| 商业 | 兑换 | NPC/任务上下文驱动 | 显示导航,不伪造兑换上下文 |
| 系统 | 设置/帮助 | 可以 | 使用移动端全屏设置页或现有控制器适配页 |
| 系统 | 选择角色/登出/退出 | 可以 | 二次确认后调用现有系统操作 |
菜单只是功能入口,不改变原服务端授权条件。
## 8. HUD 实施细节
### 8.1 `MobileHud` 兼容接口
`MobileHud` 实现与当前 `Hud` 相同的状态更新方法,并额外提供以下信号:
```gdscript
signal avatar_pressed
signal inventory_pressed
signal minimap_pressed
signal menu_pressed
signal quest_pressed(quest_id: int)
signal party_member_pressed(vid: int)
signal context_action_pressed(action: String)
```
`GameScene` 根据 UI 模式创建 `Hud``MobileHud`,并把最终对象继续传给 `NetPlay`
### 8.2 避免重复 HUD
当前 `hud.gd` 自己创建状态条、小地图占位、快捷栏和背包占位,同时 `GameScene` 还创建正式 `Quickbar``InventoryUI``Minimap`
移动模式必须明确只有一个拥有者:
- `MobileHud` 拥有首页状态、目标、任务、顶部按钮和触控布局。
- `Quickbar` 继续拥有技能槽数据、冷却和激活逻辑,但其 PC 可视节点在移动模式隐藏。
- `InventoryUI` 继续拥有背包数据和动作,但其 PC 入口隐藏。
- `Minimap` 继续提供地图数据/绘制能力,移动模式把可视根节点挂入 `MobileHud.MinimapHost` 或增加 compact 布局。
不能同时显示 PC HUD 和移动 HUD。
### 8.3 安全区与缩放
- 根布局使用 Godot anchors,不使用整页固定坐标。
- 使用 `DisplayServer.get_display_safe_area()` 获取安全区。
- 左上玩家区和右上按钮组至少保留安全区边距。
- 基准触控目标不小于 48dp;普攻主按钮建议 80–96dp。
- HUD 缩放按短边计算,并设置最小/最大范围,避免平板按钮过大。
- 字体单独分级,不跟随 PC 窗口做纯比例缩放。
## 9. 移动输入方案
### 9.1 虚拟摇杆
`PlayerController` 中增加移动轴输入,不模拟键盘事件:
```gdscript
var _mobile_axis := Vector2.ZERO
func set_mobile_axis(axis: Vector2) -> void:
_mobile_axis = axis.limit_length(1.0)
```
移动优先级建议:
```text
虚拟摇杆 > PC 键盘 > 点地移动 > 无移动
```
摇杆输入不为零时:
- 取消当前点地目标 `_is_going`
- 沿用现有相机相对方向、速度、阻挡、Actor 碰撞和动画逻辑。
- 摇杆松开后 `_mobile_axis` 清零。
- 受击、锁定、移动技能等现有移动门继续生效。
### 9.2 普攻与技能
- 普攻按钮按下调用 `NetPlay.set_attack_key(true)`,抬起或触摸取消调用 `false`
- 技能按钮点击调用 `Quickbar.activate(slot)`
- 技能长按进入瞄准态,拖动调整方向,松手调用 `Quickbar.activate_aimed()`;短按仍调用 `Quickbar.activate()`
- 方向计算、技能冷却、不可使用原因和目标选择继续由现有 `Quickbar`/`NetPlay` 处理。
- 触摸取消必须清理攻击按下状态,避免切后台后持续攻击。
### 9.3 触摸仲裁
当前 `PlayerController``GameCamera` 都接收 `InputEventScreenTouch/Drag`。新增移动控件后必须防止同一个触点同时操作 UI 和世界。
建议规则:
1. `MobileInputOverlay``_gui_input` 中记录被 UI 捕获的 touch index。
2. 被捕获的 index 不传给玩家点地和镜头控制。
3. 摇杆、技能、菜单、窗口区域调用 `get_viewport().set_input_as_handled()`
4. 未被捕获的单指仍用于点地/选中或镜头拖动。
5. 双指缩放只统计世界区域中的触点。
6. 模态窗口打开时阻止所有世界输入。
7. 应用暂停、失焦、死亡和过场时强制释放全部触点状态。
建议把捕获状态集中到一个 `TouchRouter`,不要让每个按钮各自维护全局触点。
## 10. 功能窗口适配策略
### 10.1 三种适配级别
#### A. 直接复用控制器并更换入口
适用:背包开关、大地图、任务日志、好友、公会、系统菜单。
首期由 `MobileUiRoot` 调用现有 `toggle/open/close` 方法。
#### B. 同一控制器增加移动布局
适用:队伍、快捷栏、小地图、聊天、系统设置。
保留数据和信号,给 `_build()` 增加 `layout_mode`,分别构建 desktop/mobile 视图。
#### C. 拆分逻辑和视图
适用:背包、角色、技能、商店、交易、仓库、Cube 等固定坐标和拖放较重的窗口。
逐步拆成:
```text
FeatureController:数据、信号、网络操作、校验
DesktopFeatureView:现有 PC 布局
MobileFeatureView:横屏全屏布局
```
首期允许保留现有控制器,并只在真正重做对应页面时拆分,避免一次性重构全部 UI。
### 10.2 PC 专属交互的移动替代
| PC 交互 | 移动端替代 |
|---|---|
| 鼠标悬停 tooltip | 短按选中、长按详情 |
| 右键使用/出售 | 选中后显示主操作按钮 |
| 标题栏拖动窗口 | 固定全屏或右侧面板 |
| 键盘 I/K/C/N/M | 首页入口或五类菜单 |
| 数字键/F1–F4 技能 | 右下技能盘 |
| 鼠标拖动物品 | 长按进入拖动,松手落位 |
| ESC 关闭 | 系统返回、面板关闭按钮、遮罩点击 |
## 11. `GameScene` 接入方案
建议把 UI 创建拆成三个阶段:
```gdscript
func _build_shared_feature_controllers() -> void:
# 创建背包、技能、任务、社交、商店等共享控制器
func _build_desktop_presentation() -> void:
# 创建当前 Hud、PC Quickbar、PC 窗口入口
func _build_mobile_presentation() -> void:
# 创建 MobileUiRoot、MobileHud、触控层和移动菜单
```
接入顺序:
1. 创建 `UiManager` 和共享功能控制器。
2. 检测 `UiProfile`
3. 创建 Desktop 或 Mobile 表现层。
4. 把当前 HUD 实例传给 `NetPlay`
5.`Quickbar``PlayerController``GameCamera``GroundItems` 注入 `MobileInputOverlay`
6. 将现有功能控制器注入 `MobileUiRoot` 的路由绑定。
7. 地图切换或世界重载时,只重载世界相关 HUD 数据,不重建所有功能控制器。
## 12. 开发阶段
### 阶段 M1:双 UI 骨架(已完成,待真机验收)
目标:PC UI 完全不变,桌面可强制预览移动 UI。
- 增加 `UiProfile`
- 增加 `MobileUiRoot`、安全区和横屏约束。
- `GameScene` 能在 Desktop/Mobile 表现层之间选择。
- 移动 HUD 使用假数据完成布局,但不重复显示 PC HUD。
验收:
- macOS 默认仍显示现有 PC UI。
- 强制 mobile 时只显示移动 HUD。
- Android/iOS 自动选择移动 UI。
### 阶段 M2:首页状态与入口(已完成,待真机验收)
- 接入玩家名称、HP、MP、资源、经验、等级和状态效果。
- 接入目标名称与目标 HP。
- 接入背包、小地图、头像、任务条和五类菜单。
- 接入左上队伍紧凑条。
- 接入死亡、重连、副本目标等覆盖状态。
验收:所有首页入口打开正确功能,返回后恢复场景输入。
### 阶段 M3:移动与战斗触控(代码完成,待真机验收)
- 新增虚拟摇杆连续移动。
- 新增普攻、技能、药水和情境交互按钮。
- 处理触摸 index 捕获、取消、切后台和模态窗口。
- 接入技能冷却、不可使用原因和目标选择反馈。
验收:移动、镜头、攻击、技能可同时使用且不串触摸。
### 阶段 M4:高频功能页(部分完成)
- 角色/装备/技能全屏页:角色属性/装备、技能分类和技能升级已有横屏专用布局;角色表情也提供移动触控页。
- 背包和物品详情:背包点按操作、长按详情、数字键盘数量输入、跨窗口长按拖放已完成;仍需真机确认系统手势区和键盘共存体验。
- 任务日志和大地图:已有移动托管;大地图支持单指拖动并在双指时冻结单指拖动。
- 聊天与好友:聊天/私聊已有横屏输入页、触控按钮和弹窗返回处理,真机键盘体验待验收。
验收:不依赖右键、鼠标悬停和键盘即可完成完整操作。
### 阶段 M5:商业、制作与系统(部分完成)
- 商店、商城、交易、私人商店、仓库和兑换:移动端入口/候选列表/点按操作已覆盖;服务端上下文仍需联机验收。
- Cube、精炼、龙魂入口状态:Cube 配方/材料/制作和精炼按钮已有横屏触控布局,龙魂仍按功能开关控制。
- 系统设置、游戏设置、帮助、角色切换、登出和退出:系统设置/游戏设置/帮助已有横屏专用布局;系统菜单增加移动端专用横屏入口,好友、公会创建、公会邀请、商城/仓库密码、商店确认等确认弹窗已扩大触控尺寸并接入返回处理。
- 确保所有上下文窗口只在服务端允许时打开。
### 阶段 M6:真机验收与视觉统一(未完成)
- Android 和 iOS 安全区。
- 不同屏幕比例、分辨率和 DPI。
- 多指触控、后台恢复和断线重连(代码已接入,仍需真机/联机验收)。
- GPU/CPU/内存与触控延迟。
- 统一暗色玻璃、铜金边框、字号、按钮和动画。
## 13. 测试方案
### 13.1 自动化测试(已实现)
以下移动端专项测试已经加入项目,并与现有 PC/业务测试一起运行:
- `mobile_ui_test.gd`:模式解析、横屏保护、安全区、HUD 状态、菜单分类和 UI/世界触点仲裁。
- `mobile_window_host_test.gd`:返回键、模态阻断和窗口栈。
- `mobile_gesture_test.gd`:长按/拖动触摸状态、技能瞄准、技能拖入快捷栏、快捷栏交换和物品触摸拖放。
- `chat_mobile_ui_test.gd`:横屏聊天日志、频道切换、私聊目标和数字键盘友好的输入页。
- `gamescene_test.gd`:PC/移动 UI 装配互斥和移动根节点接线。
- `char_status_ui_test.gd``inventory_ui_test.gd`:角色页、背包详情、数量输入和触摸操作。
- `p8_test.gd`:交易候选列表、仓库取出和移动端仓库存入候选列表。
- `shop_cube_mall_test.gd`:Cube 配方、材料添加/删除、制作按钮和移动端横屏布局。
- `guild_creation_ui_test.gd``guild_war_skill_test.gd``help_ui_test.gd`:公会、帮助和确认弹窗的移动布局。
- `system_menu_ui_test.gd``system_option_ui_test.gd``private_shop_ui_test.gd`:系统入口、设置和商业窗口的移动交互。
完整测试集当前共 78 个 `*_test.gd`,现有 PC UI、业务、网络、战斗和移动端测试均通过。推荐命令:
```bash
for test in $(rg --files project -g '*test.gd' | sort); do
/opt/homebrew/bin/godot --headless \
--rendering-method gl_compatibility \
--rendering-driver opengl3 \
--audio-driver Dummy \
--log-file "/tmp/mtgodot-${test##*/}.log" \
--path project --script "${test#project/}" || exit 1
done
```
### 13.2 桌面模拟验收
- 844×390、16:9 平板、超宽屏三档。
- 鼠标模拟触控验证所有移动入口。
- 强制 mobile profile 后走完登录、角色选择和游戏内 UI。
- 同一进程切换地图、死亡复活和断线重连。
### 13.3 Android/iOS 真机验收
- 刘海和系统手势区不遮挡按钮。
- 左手持续移动时,右手可选目标、普攻和连续释放技能。
- 两指缩放不会触发点地或技能。
- 拖动物品时不会移动角色或旋转镜头。
- 来电/切后台后,摇杆和普攻状态正确释放。
- 队伍成员变化、HP 和距离能实时更新。
- 商店、仓库、制作等页面不会绕过服务器上下文。
## 14. 完成标准
移动端 UI 可认为完成,必须同时满足:
1. PC UI 默认行为和快捷键无回归。
2. Android/iOS 自动进入移动 UI,桌面可强制预览。
3. 首页严格符合已确认草图布局。
4. 角色只从玩家头像进入。
5. 队伍只在头像下方显示,不在社交菜单出现。
6. 任务、小地图和背包保持首页直达。
7. 右上菜单只有社交、成长、活动、商业、系统五类。
8. 不使用键盘、鼠标右键或悬停,也能完成所有移动端操作。
9. 所有功能继续使用现有服务器授权和网络包流程。
10. 自动化测试、桌面模拟和 Android/iOS 真机测试均通过。
## 15. 改动规模与主要风险
### 15.1 改动规模
| 层级 | 改动规模 | 说明 |
|---|---|---|
| 网络协议与服务端 | 小/无 | 继续使用现有 `M2Client` 与 40250 流程 |
| 游戏状态与战斗逻辑 | 小 | 增加移动输入入口,不重写校验和攻击流程 |
| 首页 HUD | 大 | 新建移动端表现层 |
| 输入系统 | 中到大 | 摇杆、技能盘、多指仲裁是核心新增 |
| 高频功能页 | 中到大 | 背包、角色、技能、任务需重排 |
| 低频窗口 | 中 | 可分阶段适配 |
| PC UI | 小 | 原样保留,只增加模式分流 |
### 15.2 主要风险
1. **触摸穿透**:技能或摇杆触摸同时触发地面移动/镜头。
- 对策:集中式 `TouchRouter`,按 touch index 捕获。
2. **重复 HUD**:旧 HUD、Quickbar、Minimap 与新 HUD 同时显示。
- 对策:明确 Desktop/Mobile 表现层互斥创建。
3. **固定坐标窗口过小**PC Python UI 在移动端不可操作。
- 对策:高频页优先重排,低频页先用安全区容器过渡。
4. **菜单绕过上下文**:直接打开商店、仓库、精炼等造成无效状态。
- 对策:`MobileUiRoot` 区分“可直接打开”和“需要 NPC/服务器状态”。
5. **PC 回归**:共享控制器重构影响键盘和鼠标行为。
- 对策:首期新增并行表现层,拆分控制器时补双端测试。
6. **横屏安全区差异**:按钮被刘海或系统边缘手势遮挡。
- 对策:运行时读取安全区并保留可配置边距。
## 16. 推荐首个实现切片
首个可真机验证的垂直切片已经完成,当前作为移动端联调基线:
```text
UI 模式选择
→ 移动 HUD
→ 头像/背包/地图/任务/菜单入口
→ 左上队伍条
→ 虚拟摇杆
→ 普攻 + 4 个技能
→ Android 真机战斗验证
```
该切片已让客户端具备可用的移动端首页和核心战斗能力;角色、背包、技能等全屏页也已按使用频率完成移动布局,剩余工作集中在真机/联机验收和视觉微调。
## 17. 当前实现状态
截至 2026-09-04,代码已完成首个垂直切片,并继续补齐了部分商业/制作页面:
- `project/ui/ui_profile.gd``AUTO/DESKTOP/MOBILE` 模式选择,支持移动设备自动识别、`mt/ui/profile` 配置和 `--mobile-ui` / `--desktop-ui` 预览参数。
- `project/ui/mobile/mobile_ui_root.gd`:移动端 CanvasLayer 根节点,按 844×390 横屏设计基准自适应缩放并处理功能路由。
- `project/ui/mobile/mobile_hud.gd`:名称、头像、同宽 HP/MP、目标、队伍紧凑条、任务入口、背包/小地图/菜单入口。
- `project/ui/mobile/mobile_input_overlay.gd``virtual_joystick.gd``mobile_touch_button.gd`:虚拟摇杆、普攻、药水、4 个技能触控按钮、触摸取消和失焦释放。
- `project/ui/mobile/mobile_input_overlay.gd``project/player_controller.gd`:集中记录移动 UI 触点,避免“摇杆 + 世界触点”触发点地移动。
- `project/ui/mobile/mobile_menu_drawer.gd`:社交、成长、活动、商业、系统五类两级菜单。
- `project/player_controller.gd`:新增移动轴输入,沿用现有相机相对移动、阻挡、动画和状态门。
- `project/ui/char_status_ui.gd`:角色属性/装备和表情移动横屏页面,属性点仍走原 `/stat` 命令。
- `project/ui/skill_ui.gd``project/ui/quickbar.gd`:技能移动横屏列表、升级和一键放入快捷栏空位。
- `project/ui/inventory_ui.gd`:物品长按详情、使用/选择操作和移动端数字键盘数量输入。
- `project/ui/chat_ui.gd`:横屏聊天日志、频道切换和私聊输入页。
- `project/ui/system_option_ui.gd``project/ui/game_option_ui.gd`:横屏系统/游戏设置页,保留原配置和服务器命令。
- `project/ui/help_ui.gd``project/ui/system_menu_ui.gd`:横屏帮助页和移动模式下的帮助路由。
- `project/ui/minimap.gd`:复用现有小地图绘制和数据,移动模式挂载到右上 HUD;点击地图仍打开大地图。
- `project/ui/party_ui.gd``quickbar.gd``quest_log.gd`:移动模式隐藏 PC 表现层,保留原数据和业务控制器。
- `project/game_scene.gd`:PC/移动表现层互斥装配,移动端控制器注入统一路由;PC HUD 默认路径保持不变。
- `project/ui/safebox_ui.gd`:移动端背包候选列表,点按存入、点按取出和重复请求保护。
- `project/ui/cube_ui.gd`:移动端横屏双列滚动布局,配方、材料添加/删除和制作按钮均可触摸。
- `project/ui/refine_ui.gd`:移动端横屏精炼信息与操作布局。
- `project/ui/guild_ui.gd`:移动端横屏公会四页布局、成员/技能/公会战/公告的触控按钮和创建公会弹窗。
- `project/ui/system_menu_ui.gd`:保留桌面原始系统菜单,移动端使用横屏专用系统菜单,入口和服务器命令保持一致。
- `project/ui/friend_ui.gd``project/game_scene.gd``project/ui/shop_ui.gd``project/ui/mall_ui.gd``project/ui/safebox_ui.gd`:确认弹窗移动触控尺寸与 Android 返回优先取消处理。
- `project/ui/ui_manager.gd``project/ui/mobile/mobile_window_host.gd`:动态 Button 触摸桥接、原生确认弹窗登记、模态阻断、返回顺序和安全区变化重排。
- `project/ui/reconnect_ui.gd``project/app_flow.gd`:游戏内断线保留 `GameScene`,显示移动重连遮罩,自动重新进入上次角色。
- `project/project.godot`:增加移动 UI 配置项和横屏方向设置。
当前验证结果:
- 全部 78 个 `*_test.gd` 通过,包括 `app_flow_lifecycle_test.gd``p10_test.gd``mobile_ui_test.gd``mobile_window_host_test.gd``mobile_gesture_test.gd`Godot 脚本解析检查通过。
尚未完成的部分包括:Android/iOS 真机安全区、不同屏幕比例、多指、后台恢复、性能与触控延迟验收,以及商业页面在真实服务器上下文下的联机验收。断线重连的代码路径已经完成:游戏中断线会保留当前 `GameScene`、显示遮罩并自动重新进入上次角色,但仍需要在真机和真实服务器上验证网络切换、后台恢复及重连失败边界。低频页面仍需要在真机上做字号、键盘和安全区的视觉微调;当前不能据此视为最终移动端交付完成。