Files
TankIC/3DTank/docs/UI_Rebuild_Plan.md
T

15 KiB
Raw Blame History

TankiOffline UI 重建计划书(全站 UI)

目标:把原客户端(module_82347 大厅/车库/商店/军团/结算 UI + module_21300 战斗 HUD) 还原到 Kotlin/JS 新工程,最终覆盖全站所有界面,并与真实状态 / 协议数据打通。 本文件用于对齐范围、技术栈、里程碑与验收口径。

0. 已确认决策(2026-10-25)

议题 决策
技术栈 Kotlin/JS + kotlin-react + Emotion(与现工程同栈)
还原精度 组件级还原;背景/摄像机是动态的,不做像素 diff,由人工验收,尽可能贴近
数据源 本地 mock 服务器(协议级 mock,稳定产出用户/物品/大厅数据)
原版账号 有(WYT2WYT),仅用于线上获取必要信息(DOM/CSS/资源/文案),不做逐屏截图对拍
3D 现在接 WebGL:先做 3D 资源加载器与车库预览,再逐步接 92980 渲染管线
首批屏幕 大厅骨架 + 车库
资源部署 开发用本地镜像;生产走 CDN / 反向代理
混跑策略 允许长期保留部分原 JS UI,按屏幕逐步替换为 Kotlin

1. 调研结论(先纠正三个认知)

1.1 原前端不是手写 React,而是 Kotlin/JS + React + Emotion

证据:

证据 说明
module_glossary:78931 = React 18.3.1、45555 = React-DOM 18.3.1 运行时确实是 React
94090 = “React 封装的内部 UI 构建工具 RBuilder” Kotlin 侧的 React DSL
82347 的代码形态:Sq(d7n, "MainScreenComponentStyle", $q, h$)、this.aux([], K2n)、Eq("containerPanel", 1, …) 这是 Kotlin/JS 编译后的 styled-component 构造,不是手写 JS
类名如 MainScreenComponentStyle-containerPanel、RoundBigButtonComponentStyle-outerCircle Emotion 生成的组件样式名
82347 依赖 10032 / 18334 / 61860 / 87072 / 92980 / 61378 / 85196 … UI 直接吃状态、服务、渲染、战斗系统

结论:另起一个 React/TS 工程不是“更原版”,反而会丢掉 Kotlin 数据模型。 推荐在现有 Kotlin/JS 工程里加 kotlin-react + Emotion,逐屏移植;纯 React/TS 只作为备选。

1.2 原 UI 的体量与耦合

  • 82347:5.50 MB,约 21.3 万行,覆盖大厅 / 车库 / 商店 / 军团 / 好友 / 新闻 / 设置 / 结算 / 弹窗。
  • 21300:战斗内 HUD(血条、准星、道具、计分板、聊天…)。
  • 大量组件直接依赖:
    • 状态:RootState 的 89 个切片(已迁移 89/89);
    • 服务:18334 的 509 处动作派发、物品目录(已迁移 ItemCatalog);
    • 渲染:92980 WebGL(车库 3D 预览、战斗场景);
    • 战斗:87072(HUD 数据)。
  • 因此 UI 必须分层:布局/样式可先独立还原,数据和 3D 后接。

1.3 资源已经齐了

  • 镜像:3DTank/web/play/static(1239 文件 / 71.4 MB,含 images / fonts / translations / sound / videos / textdata)。
  • 已复制到:TankiOffline/app/src/jsMain/resources/play/static,开发服务器直接以 /play/static/... 提供。
  • 官方翻译:/play/static/translations/zh-CN.61d6ab60.json(运行时加载,已接)。
  • 可直接引用的资源 id → 路径:3DTank/src/resources.json 的 resourceMap。
  • 可选:3DTank/Start-LocalAndRelay.ps1 启动镜像 + 中继(抓包/真实服务器)。

2. 技术方案

2.1 推荐方案 A:Kotlin/JS + kotlin-react + Emotion(与现工程同栈)

TankiOffline/
  ui/                      # 新:React 组件(Kotlin)
    src/jsMain/kotlin/Ui/
      Theme.kt             # 颜色/字体/间距 token(从原 Emotion 样式抽出)
      Components/          # Button / Panel / Tabs / Dialog / ItemCard ...
      Screens/             # Lobby / Garage / Shop / Battles / Quests ...
      Layout/              # MainScreen / Header / Menu / Chat / Footer
  app/                     # 入口:Entrance → 连接 → Lobby
  state/                   # 已有 RootState
  net/                     # 已有协议

依赖:

implementation(npm("react", "18.3.1"))
implementation(npm("react-dom", "18.3.1"))
implementation(npm("@emotion/react", "11.x"))
// Kotlin 侧可用 kotlin-react / kotlin-emotion 包装,或直接 external 声明

数据流:

SessionRouter (Store) ──订阅──> UiStore (Adapter) ──props──> React 组件
ItemCatalog ───────────────────┘
Session 状态(连接/帧/日志)─────┘

优点:复用现有状态类;一套 Gradle 构建;后续可直接接 3D/战斗。 缺点:Kotlin React 组件写法与原始编译产物不完全一致,需要逐屏翻译。

2.2 备选方案 B:独立 React/TS 工程

webui/ (Vite + React + TS + Emotion)
  ↕ WebSocket / postMessage
TankiOffline (Kotlin/JS 客户端)

优点:前端生态成熟、UI 开发快、便于像素级还原。 缺点:状态模型要跨语言复制/序列化;构建两套;长期维护成本高。

2.3 备选方案 C:混跑原 JS 模块

直接把 module_82347 作为 external 挂到 window.__modules__,用现有 Kotlin 状态通过 bridge 驱动。 优点:保真度最高、最快看到完整原版 UI。 缺点:不是“重建”,依赖原 bundle;长期难以维护。 可作为 过渡验收(视觉基准)而不是最终形态。

2.4 保真度分层

级别 内容 验收
L0 结构骨架(当前) 能导航、无报错
L1 官方资源 / 字体 / 布局比例 截图肉眼接近
L2 像素级静态还原 与原版截图逐屏 diff
L3 真实数据 + 交互 状态变化驱动 UI
L4 3D 车库 / 战斗场景 WebGL 管线接入

3. 里程碑

M0 基础设施(进行中)

  • 资源镜像接入(/play/static)。
  • 登录页 1:1(EntranceView + entrance.css)。
  • ui 模块启用 kotlin-react + Emotion,建立 Theme.kt。
  • 协议级 mock 服务器(用户属性 / 物品目录 / 大厅列表)。
  • ScreenRegistry:屏幕 ID ↔ 组件(对齐原 ScreenId)。
  • UiStore:订阅 SessionRouter.Store,节流重绘。
  • 3D 资源加载器(.3ds / 贴图 / 场景描述)+ 车库预览最小场景。
  • 截图 / DOM 基准工具(人工验收辅助,不做像素门禁)。

M1 大厅骨架(2 个迭代)

  • 顶栏:等级/昵称/GS/水晶/金币/VIP/设置/声音。
  • 左侧菜单:9 个官方图标 + 文案 + 选中态。
  • 底部:大号“开始战斗”按钮、聊天面板、新闻条。
  • 公告/活动/每日任务浮层。
  • 验收:与官方大厅截图逐项对齐(无 3D 场景时用静态背景占位)。

M2 车库(3 个迭代)

  • 分类页签(12 类,来自 ItemCategory 枚举)。
  • 物品网格:名称/稀有度/数量/VIP/价格;数据源 ItemCatalog。
  • 物品详情:属性、升级、装备、皮肤。
  • 3D 预览:接 M0 的 3D 加载器,先渲染底盘/炮塔/贴图与旋转,再补光照/摄像机。

M2.5 3D / 渲染(并行)

  • 3DS / 贴图 / 资源清单加载。
  • 车库场景:平台、背景、坦克组装、光照、摄像机环绕。
  • 与 92980 渲染管线的接口对齐;最终替换为原引擎实现。

M3 商店 / 幸运礼盒(2 个迭代)

  • 商店分类、卡片、折扣、支付方式、礼盒开启动画占位。
  • 数据:Shop 切片 + ItemCatalog。

M4 战斗选择 / 任务 / 军团 / 好友 / 新闻(3 个迭代)

  • 战斗模式卡片、战斗列表、创建战场表单。
  • 任务:主线/每日/每周/星级挑战/合约。
  • 军团/好友:列表、搜索、请求、成员管理。
  • 新闻:列表 + 详情。

M5 设置 / 弹窗体系(2 个迭代)

  • 设置 8 个页签(游戏/画面/声音/操控/账户/战斗/安全/支持)。
  • 通用 Dialog / Toast / 确认框 / 购买弹窗 / 升级弹窗 / 排名弹窗。

M6 结算与 HUD(依赖 S5)

  • 战斗结算、奖励、排名变化。
  • 战斗 HUD(21300):血条、道具、准星、计分板、聊天。

4. 每屏的数据契约(先定,避免返工)

屏幕 主要数据 现有来源 缺口
Entrance 语言/资源/连接状态 Translations / LobbyClient 真实登录
Lobby Home User / News / Notifications / Quests 摘要 RootState 已迁移 公告、活动配置
Garage ItemCatalog / Garage 切片 / GaragePreview 已有 3D 预览、装备操作
Shop Shop 切片 / ItemCatalog 已有 支付流程
Battles BattlesSelect / Maps / Matchmaking 已有 创建/匹配
Quests Quests / Challenges / ContractState 已有 领奖链路
Clans Clan / ClanStorage / ClanChat 已有 社交操作
Friends Friends 切片 已有 搜索/请求
News News 切片 已有 详情页
Settings 8 个设置切片 已有 持久化 port
HUD BattleStatistics / BattleUsers / Supplies 部分 S5 战斗系统

5. 验收方式

  1. 视觉基准:原版客户端逐屏截图(需要可登录环境);新 UI 同分辨率截图,人工/像素 diff。
  2. DOM 基准:同一数据下对比关键元素的尺寸、间距、颜色 token。
  3. 交互基准:点击路径一致(菜单、返回、弹窗层级)。
  4. 数据基准:同一抓包回放后,界面显示与状态断言一致(可自动化)。

6. 风险与待决事项

风险 影响 对策
没有可登录账号,拿不到原版大厅截图/DOM 无法做像素级对比 用测试账号 / 本地 mock 服务器 / 仅按源码还原
3D 车库与战斗场景 大厅“灵魂”缺失 先用静态背景 + 官方素材,S4 后替换
82347 21 万行,逐屏翻译量大 周期长 按 M1–M5 切片,先布局后逻辑
原样式是 Emotion 动态类名 难以直接复用 CSS 抽 token + 按截图复写;或运行时抓取 CSS
资源 71 MB 进包 首屏体积 开发用镜像;生产走 CDN/代理

7. 当前进度(本文件创建时)

  • 登录页:1:1 完成(官方背景、logo、圆形登录按钮、健康忠告、年龄提示、ClientInfo)。
  • 大厅:L0/L1 骨架(官方图标、真实翻译、物品目录、连接状态)。
  • 资源:本地镜像已接入。
  • 已确认:技术栈 Kotlin/React、组件级还原、本地 mock、3D 现在做、首批大厅+车库、允许混跑。

9. 原版 DOM 实测映射(2026-10-25,登录 WYT2WYT 后)

视口 1296×1006,根字号 12.96px(.ksc-37 的 max(min(1.48148vh,1vw),3px))。 所有尺寸换算:1em ≈ 12.96px。

9.1 大厅首页

原 DOM 类 位置 / 尺寸 含义
MainScreenComponentStyle-containerPanel 0,0 1296×78 顶栏(6em,底边 1px 白 25%)
UserInfoContainerStyle-blockLeftPanel 0,0 408×78 等级图标 / 组队图标 / 昵称 / XP
UserInfoContainerStyle-rankIconContainerClickable 155,0 78×78 等级图标(static/images/<rank>.<hash>.webp)
UserInfoContainerStyle-containerProgressMainScreen 246,15 162×49 昵称 + XP 条 + 数值
UserScoreComponentStyle-blockRightPanel 1017,0 279×78 两行货币(红宝石 / 水晶)+ 全屏 + 退出
MainScreenComponentStyle-containerForMenuGradient 0,0 648×1006 左侧 50%,黑→透明 0.5
MainScreenComponentStyle-playButtonContainer 57,130 284×139 “开始战斗”,背景 videoplay.79570900.gif
MainScreenComponentStyle-blockMainMenu 57,283 324×645 菜单容器
PrimaryMenuItemComponentStyle-menuItemContainer 65,283 350×58 菜单项:礼盒 / 任务 / 车库 / 商店 / 设置
PrimaryMenuItemComponentStyle-itemCommonLi 73,290 45×45 图标(mask + #bfd5ff)
PrimaryMenuItemComponentStyle-itemName 133,293 2em 大写文字
FooterComponentStyle-footer 0,928 648×78 新闻 / 聊天 / 好友 / 军团(65×65)
MainScreenComponentStyle-gradientBlockRight 936,0 360×1006 右侧渐变(27.75em,0.75,旋转 180°)
BattlePassLobbyComponentStyle-menuBattlePass 972,110 292×130 星级挑战面板(22.5em×10em,right 2.5em)
AnnouncementHomeScreenComponentStyle-mainContainer 971,778 293×196 公告卡(22.5em 宽,right/bottom 2.5em)
ClientInfoComponentStyle-main 0,989 1296×17 UID / 版本 / 电池

9.2 车库

原 DOM 类 位置 / 尺寸 含义
BreadcrumbsComponentStyle-headerContainer 0,0 1296×78 车库顶栏(返回 / 标题 / 货币 / 退出)
BreadcrumbsComponentStyle-backButton 0,0 78×78 返回
BreadcrumbsComponentStyle-rootTitle 585,2 126×73 当前屏幕标题
GarageCommonStyle-garageContainer 0,78 1296×928 车库主体
GarageCommonStyle-positionContent 0,130 1296×876 内容区
GarageMainScreenStyle-blockParameters 78,130 1140×685 中央 3D 预览 + 左右参数
TankParametersStyle-leftParamsContainer 78,130 293×209 左上:GS + 参数
MountedItemsStyle-container 920,130 298×492 右上:无人机 / 手榴弹 / 炮塔 / 底盘
MountedItemsStyle-resistanceContainer 78,740 52×65 ×4 左下:防御模块
SuppliesComponentStyle-commonBlockSupplies 78,862 1140×125 底部道具栏
TankPreviewComponentStyle-visible GarageComponentStyle-tankPreview 0,0 1296×1006 3D 坦克画布

9.3 对应关系(JS → DOM)

  • 82347 里 Sq(Class, "XxxComponentStyle", ...) 注册的样式类,就是 DOM 上的 XxxComponentStyle-*。
  • Eq("containerPanel", 1, ...) 返回的类名拼接为 MainScreenComponentStyle-containerPanel。
  • 样式值直接写在 jq(Class).xxx = function(){ return this.<slot>.dk(this, Eq("...")) } 的样式对象里; 运行时可从 document.styleSheets 按类名前缀抓取(已用于本表)。
  • 组件层级:MainScreen → Header / PrimaryMenu / Footer / BattlePass / Announcement; 车库:GarageCommon → TankParameters / MountedItems / Supplies / TankPreview。

9.4 当前实现状态

  • 大厅首页已按上表重建(6em 顶栏、21.875em Play、27em 菜单、5em 底栏、22.5em 右栏)。
  • 车库正在按 9.2 重建:左侧参数 / 中央 3D / 右侧已装配件 / 底部道具栏; 物品选择列表作为抽屉保留,后续替换为原版的分类轮盘。
  • Kotlin/React 工具链接通(kotlin-react / kotlin-react-dom / kotlin-emotion,React 19.1)。
  • 物品目录 fixture:SessionCheck 从抓包导出 resources/fixtures/catalog.json(1399 物品)。
  • 车库第一版 React 界面:分类页签(12 类)、搜索、物品网格(120 张卡片)、右侧详情位。
  • 车库按 9.2 的原版布局重建 + 3D 预览加载器。
  • 协议级 mock 服务器(用户 / 大厅列表 / 任务)。
  • 大厅其余屏幕迁移到 React。