|
|
@@ -0,0 +1,1253 @@
|
|
|
+# CMR Mini 开发架构阶段总结
|
|
|
+
|
|
|
+本文档用于记录当前阶段小程序的整体架构、分层原则、事件驱动链路、模拟器体系,以及后续继续扩展时应遵守的边界。
|
|
|
+
|
|
|
+当前阶段的核心目标已经从“把地图画出来”升级为“建立一套可长期扩展的运动地图游戏底座”。
|
|
|
+这套底座已经具备以下关键能力:
|
|
|
+
|
|
|
+- 地图引擎与玩法规则解耦
|
|
|
+- 通用 telemetry 信息层独立
|
|
|
+- 声音 / 震动 / UI 动效统一走反馈层
|
|
|
+- 真实与模拟 GPS 双源并存
|
|
|
+- 真实与模拟心率双源并存
|
|
|
+- 顺序赛与积分赛两套规则可共存
|
|
|
+- 外部模拟器可以在室内驱动定位与心率联调
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1. 项目当前总体分层
|
|
|
+
|
|
|
+当前工程可以概括为 6 层:
|
|
|
+
|
|
|
+1. 内容与配置层
|
|
|
+2. 地图引擎层
|
|
|
+3. 规则运行时层
|
|
|
+4. Telemetry 信息层
|
|
|
+5. Presentation 展示适配层
|
|
|
+6. Feedback 反馈层
|
|
|
+
|
|
|
+此外,还有一条独立的开发调试链:
|
|
|
+
|
|
|
+- 小程序调试面板
|
|
|
+- 外部模拟器 `tools/mock-gps-sim`
|
|
|
+
|
|
|
+这两者不参与业务规则,只作为开发辅助工具。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2. 目录结构说明
|
|
|
+
|
|
|
+### 2.1 小程序主目录
|
|
|
+
|
|
|
+位于 [miniprogram](D:/dev/cmr-mini/miniprogram):
|
|
|
+
|
|
|
+- [engine](D:/dev/cmr-mini/miniprogram/engine)
|
|
|
+- [game](D:/dev/cmr-mini/miniprogram/game)
|
|
|
+- [pages](D:/dev/cmr-mini/miniprogram/pages)
|
|
|
+- [utils](D:/dev/cmr-mini/miniprogram/utils)
|
|
|
+- [assets](D:/dev/cmr-mini/miniprogram/assets)
|
|
|
+
|
|
|
+### 2.2 游戏相关目录
|
|
|
+
|
|
|
+位于 [miniprogram/game](D:/dev/cmr-mini/miniprogram/game):
|
|
|
+
|
|
|
+- [core](D:/dev/cmr-mini/miniprogram/game/core):规则运行时核心模型
|
|
|
+- [rules](D:/dev/cmr-mini/miniprogram/game/rules):玩法插件
|
|
|
+- [presentation](D:/dev/cmr-mini/miniprogram/game/presentation):展示适配状态
|
|
|
+- [telemetry](D:/dev/cmr-mini/miniprogram/game/telemetry):通用信息层
|
|
|
+- [feedback](D:/dev/cmr-mini/miniprogram/game/feedback):反馈统一入口
|
|
|
+- [audio](D:/dev/cmr-mini/miniprogram/game/audio):声音层
|
|
|
+- [content](D:/dev/cmr-mini/miniprogram/game/content):课程内容转游戏定义
|
|
|
+
|
|
|
+### 2.3 地图引擎目录
|
|
|
+
|
|
|
+位于 [miniprogram/engine](D:/dev/cmr-mini/miniprogram/engine):
|
|
|
+
|
|
|
+- [map](D:/dev/cmr-mini/miniprogram/engine/map):宿主编排与地图主入口
|
|
|
+- [renderer](D:/dev/cmr-mini/miniprogram/engine/renderer):WebGL/2D 渲染
|
|
|
+- [sensor](D:/dev/cmr-mini/miniprogram/engine/sensor):GPS、心率、罗盘、模拟源
|
|
|
+- [tile](D:/dev/cmr-mini/miniprogram/engine/tile):瓦片缓存与加载
|
|
|
+- [camera](D:/dev/cmr-mini/miniprogram/engine/camera):相机与坐标换算
|
|
|
+- [overlay](D:/dev/cmr-mini/miniprogram/engine/overlay):地图叠加层相关
|
|
|
+- [layer](D:/dev/cmr-mini/miniprogram/engine/layer):图层定义
|
|
|
+
|
|
|
+### 2.4 外部模拟器目录
|
|
|
+
|
|
|
+位于 [tools/mock-gps-sim](D:/dev/cmr-mini/tools/mock-gps-sim):
|
|
|
+
|
|
|
+- [server.js](D:/dev/cmr-mini/tools/mock-gps-sim/server.js):本地 HTTP + WebSocket 服务
|
|
|
+- [public/index.html](D:/dev/cmr-mini/tools/mock-gps-sim/public/index.html):模拟器 UI
|
|
|
+- [public/simulator.js](D:/dev/cmr-mini/tools/mock-gps-sim/public/simulator.js):地图、路径、心率模拟逻辑
|
|
|
+- [public/style.css](D:/dev/cmr-mini/tools/mock-gps-sim/public/style.css):布局与样式
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3. 设计原则
|
|
|
+
|
|
|
+### 3.1 地图引擎只负责地图能力
|
|
|
+
|
|
|
+地图引擎负责:
|
|
|
+
|
|
|
+- 地图缩放、平移、旋转
|
|
|
+- 真实 GPS 与模拟 GPS 的接入
|
|
|
+- 真实心率带与模拟心率的接入编排
|
|
|
+- 路线、控制点、轨迹、编号的绘制
|
|
|
+- WebGL 与 2D 文本层的渲染同步
|
|
|
+- 把规则层产出的 map presentation 转成地图显示
|
|
|
+
|
|
|
+地图引擎不负责:
|
|
|
+
|
|
|
+- 当前玩法规则判定
|
|
|
+- 计分
|
|
|
+- 完成条件
|
|
|
+- 打点策略
|
|
|
+- 游戏胜负
|
|
|
+
|
|
|
+核心入口文件是 [mapEngine.ts](D:/dev/cmr-mini/miniprogram/engine/map/mapEngine.ts)。
|
|
|
+
|
|
|
+### 3.2 规则层只负责玩法状态推进
|
|
|
+
|
|
|
+规则层只关心:
|
|
|
+
|
|
|
+- 游戏事件输入
|
|
|
+- 当前 session state
|
|
|
+- 规则推进后的 next state
|
|
|
+- 产出展示所需的 presentation
|
|
|
+- 产出反馈所需的 effects
|
|
|
+
|
|
|
+当前已实现两种玩法:
|
|
|
+
|
|
|
+- `classic-sequential`
|
|
|
+- `score-o`
|
|
|
+
|
|
|
+对应规则文件:
|
|
|
+
|
|
|
+- [classicSequentialRule.ts](D:/dev/cmr-mini/miniprogram/game/rules/classicSequentialRule.ts)
|
|
|
+- [scoreORule.ts](D:/dev/cmr-mini/miniprogram/game/rules/scoreORule.ts)
|
|
|
+
|
|
|
+### 3.3 通用运行信息独立为 telemetry
|
|
|
+
|
|
|
+Telemetry 层不属于具体玩法,也不属于地图引擎。
|
|
|
+
|
|
|
+它负责:
|
|
|
+
|
|
|
+- 用时
|
|
|
+- 距离
|
|
|
+- 当前速度
|
|
|
+- 平均速度
|
|
|
+- 当前目标距离
|
|
|
+- 心率
|
|
|
+- 卡路里
|
|
|
+- 精度
|
|
|
+- HUD 的通用体能颜色分级
|
|
|
+
|
|
|
+入口文件:
|
|
|
+
|
|
|
+- [telemetryRuntime.ts](D:/dev/cmr-mini/miniprogram/game/telemetry/telemetryRuntime.ts)
|
|
|
+
|
|
|
+### 3.4 展示状态独立为 presentation
|
|
|
+
|
|
|
+当前展示层已经拆成两块:
|
|
|
+
|
|
|
+- `map presentation`
|
|
|
+- `hud presentation`
|
|
|
+
|
|
|
+这样规则层可以分别决定:
|
|
|
+
|
|
|
+- 地图该怎么画
|
|
|
+- HUD 该显示什么
|
|
|
+
|
|
|
+而不让渲染器自己猜玩法语义。
|
|
|
+
|
|
|
+文件:
|
|
|
+
|
|
|
+- [presentationState.ts](D:/dev/cmr-mini/miniprogram/game/presentation/presentationState.ts)
|
|
|
+- [mapPresentationState.ts](D:/dev/cmr-mini/miniprogram/game/presentation/mapPresentationState.ts)
|
|
|
+- [hudPresentationState.ts](D:/dev/cmr-mini/miniprogram/game/presentation/hudPresentationState.ts)
|
|
|
+
|
|
|
+### 3.5 反馈统一走 effect 消费
|
|
|
+
|
|
|
+声音、震动、UI 动效不是直接写在规则里,也不是直接写在页面里,而是由规则层产出 `GameEffect[]`,再交给反馈层消费。
|
|
|
+
|
|
|
+反馈层入口:
|
|
|
+
|
|
|
+- [feedbackDirector.ts](D:/dev/cmr-mini/miniprogram/game/feedback/feedbackDirector.ts)
|
|
|
+
|
|
|
+目前已经挂入:
|
|
|
+
|
|
|
+- 声音层
|
|
|
+- 震动层
|
|
|
+- 页面动效层
|
|
|
+- 地图瞬时特效层
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. 运行主链路
|
|
|
+
|
|
|
+当前整套系统的主链路如下:
|
|
|
+
|
|
|
+```text
|
|
|
+远程配置 / KML / 静态内容
|
|
|
+-> GameDefinition
|
|
|
+-> 传感输入 (GPS / 心率 / 罗盘 / 模拟源)
|
|
|
+-> MapEngine 编排
|
|
|
+-> GameRuntime / RulePlugin
|
|
|
+-> GameSessionState + Presentation + Effects
|
|
|
+-> Renderer / HUD / FeedbackDirector
|
|
|
+```
|
|
|
+
|
|
|
+更细一点可以拆成两条并行链。
|
|
|
+
|
|
|
+### 4.1 模块关系图
|
|
|
+
|
|
|
+```mermaid
|
|
|
+flowchart LR
|
|
|
+ RC["远程配置 / KML / 静态内容"] --> GD["GameDefinition"]
|
|
|
+ GD --> GR["GameRuntime / RulePlugin"]
|
|
|
+
|
|
|
+ GPS["真实 GPS"] --> LC["LocationController"]
|
|
|
+ MGPS["模拟 GPS"] --> LC
|
|
|
+ BLE["真实心率带"] --> HR["HeartRateInputController"]
|
|
|
+ MHR["模拟心率"] --> HR
|
|
|
+ COMP["罗盘 / Heading"] --> ME["MapEngine"]
|
|
|
+
|
|
|
+ LC --> ME
|
|
|
+ HR --> ME
|
|
|
+
|
|
|
+ ME --> GE["GameEvent"]
|
|
|
+ GE --> GR
|
|
|
+
|
|
|
+ ME --> TE["TelemetryEvent"]
|
|
|
+ TE --> TR["TelemetryRuntime"]
|
|
|
+
|
|
|
+ GR --> GS["GameSessionState"]
|
|
|
+ GR --> GP["GamePresentation"]
|
|
|
+ GR --> FX["GameEffect[]"]
|
|
|
+
|
|
|
+ GS --> ME
|
|
|
+ GP --> ME
|
|
|
+
|
|
|
+ TR --> TP["TelemetryPresentation"]
|
|
|
+ TP --> HUD["HUD / 状态色 / 体能面板"]
|
|
|
+
|
|
|
+ ME --> RENDER["Renderer / WebGL / Label Canvas"]
|
|
|
+ FX --> FB["FeedbackDirector"]
|
|
|
+ FB --> SOUND["声音"]
|
|
|
+ FB --> HAPTIC["震动"]
|
|
|
+ FB --> UIFX["UI / 地图特效"]
|
|
|
+
|
|
|
+ EXT["外部模拟器"] --> MGPS
|
|
|
+ EXT --> MHR
|
|
|
+```
|
|
|
+
|
|
|
+### 4.2 玩法链
|
|
|
+
|
|
|
+```text
|
|
|
+地图点击 / GPS 更新 / 打点按钮
|
|
|
+-> GameEvent
|
|
|
+-> GameRuntime.dispatch()
|
|
|
+-> RulePlugin.reduce()
|
|
|
+-> nextState + presentation + effects
|
|
|
+```
|
|
|
+
|
|
|
+### 4.3 信息链
|
|
|
+
|
|
|
+```text
|
|
|
+GPS / 心率 / session 状态
|
|
|
+-> TelemetryEvent
|
|
|
+-> TelemetryRuntime
|
|
|
+-> TelemetryState
|
|
|
+-> TelemetryPresentation
|
|
|
+-> HUD / 颜色 / 卡路里 / 距离
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. 核心模块说明
|
|
|
+
|
|
|
+## 5.1 MapEngine
|
|
|
+
|
|
|
+文件:
|
|
|
+
|
|
|
+- [mapEngine.ts](D:/dev/cmr-mini/miniprogram/engine/map/mapEngine.ts)
|
|
|
+
|
|
|
+职责:
|
|
|
+
|
|
|
+- 宿主编排器
|
|
|
+- 不直接写玩法判断
|
|
|
+- 管理地图视图状态
|
|
|
+- 编排 `LocationController`
|
|
|
+- 编排 `HeartRateInputController`
|
|
|
+- 编排 `CompassHeadingController`
|
|
|
+- 编排 `GameRuntime`
|
|
|
+- 编排 `TelemetryRuntime`
|
|
|
+- 编排 `FeedbackDirector`
|
|
|
+- 汇总成 `MapEngineViewState` 透传给页面
|
|
|
+
|
|
|
+这里是全局协调中心,但不是业务大杂烩。
|
|
|
+
|
|
|
+应继续坚持:
|
|
|
+
|
|
|
+- 新玩法不要往这里塞规则逻辑
|
|
|
+- 新传感器/模拟器可以继续从这里编排接入
|
|
|
+
|
|
|
+## 5.2 GameRuntime
|
|
|
+
|
|
|
+文件:
|
|
|
+
|
|
|
+- [gameRuntime.ts](D:/dev/cmr-mini/miniprogram/game/core/gameRuntime.ts)
|
|
|
+
|
|
|
+职责:
|
|
|
+
|
|
|
+- 载入 `GameDefinition`
|
|
|
+- 根据 `mode` 解析具体规则插件
|
|
|
+- 对外提供统一 `dispatch(event)`
|
|
|
+- 维护:
|
|
|
+ - `state`
|
|
|
+ - `presentation`
|
|
|
+ - `mapPresentation`
|
|
|
+ - `hudPresentation`
|
|
|
+
|
|
|
+当前支持:
|
|
|
+
|
|
|
+- 顺序打点规则
|
|
|
+- 积分赛规则
|
|
|
+
|
|
|
+这是后续继续加玩法的入口。
|
|
|
+
|
|
|
+## 5.3 TelemetryRuntime
|
|
|
+
|
|
|
+文件:
|
|
|
+
|
|
|
+- [telemetryRuntime.ts](D:/dev/cmr-mini/miniprogram/game/telemetry/telemetryRuntime.ts)
|
|
|
+
|
|
|
+职责:
|
|
|
+
|
|
|
+- 消费 `gps_updated`
|
|
|
+- 消费 `heart_rate_updated`
|
|
|
+- 同步 session 状态
|
|
|
+- 计算:
|
|
|
+ - elapsed
|
|
|
+ - mileage
|
|
|
+ - distanceToTarget
|
|
|
+ - speed
|
|
|
+ - averageSpeed
|
|
|
+ - calories
|
|
|
+ - heart rate tone
|
|
|
+
|
|
|
+当前心率 / 速度逻辑:
|
|
|
+
|
|
|
+- 有心率时,颜色和卡路里优先走心率逻辑
|
|
|
+- 无心率时,自动回落到速度代理区间
|
|
|
+
|
|
|
+## 5.4 FeedbackDirector
|
|
|
+
|
|
|
+文件:
|
|
|
+
|
|
|
+- [feedbackDirector.ts](D:/dev/cmr-mini/miniprogram/game/feedback/feedbackDirector.ts)
|
|
|
+
|
|
|
+职责:
|
|
|
+
|
|
|
+- 消费 `GameEffect[]`
|
|
|
+- 分发到:
|
|
|
+ - `SoundDirector`
|
|
|
+ - `HapticsDirector`
|
|
|
+ - `UiEffectDirector`
|
|
|
+
|
|
|
+当前后台音频方案已经暂时回退成前台-only。
|
|
|
+原因是小程序后台音频 loop 行为不稳定,当前阶段不再强行实现。
|
|
|
+
|
|
|
+## 5.5 LocationController
|
|
|
+
|
|
|
+文件:
|
|
|
+
|
|
|
+- [locationController.ts](D:/dev/cmr-mini/miniprogram/engine/sensor/locationController.ts)
|
|
|
+
|
|
|
+职责:
|
|
|
+
|
|
|
+- 管理真实 GPS 与模拟 GPS 双源
|
|
|
+- 暴露统一的位置更新
|
|
|
+- 管理 mock bridge URL、连接状态、调试状态
|
|
|
+
|
|
|
+当前支持:
|
|
|
+
|
|
|
+- `real`
|
|
|
+- `mock`
|
|
|
+
|
|
|
+并且 mock GPS 已经可以通过外部模拟器驱动。
|
|
|
+
|
|
|
+## 5.6 HeartRateInputController
|
|
|
+
|
|
|
+文件:
|
|
|
+
|
|
|
+- [heartRateInputController.ts](D:/dev/cmr-mini/miniprogram/engine/sensor/heartRateInputController.ts)
|
|
|
+
|
|
|
+这是最近新增的一层,职责是统一真实心率带与模拟心率源。
|
|
|
+
|
|
|
+它内部编排:
|
|
|
+
|
|
|
+- `HeartRateController`:真实 BLE 心率带
|
|
|
+- `MockHeartRateBridge`:模拟心率桥
|
|
|
+
|
|
|
+对上游暴露统一接口:
|
|
|
+
|
|
|
+- 心率值
|
|
|
+- 状态文本
|
|
|
+- 连接状态
|
|
|
+- 设备列表
|
|
|
+- 源模式调试状态
|
|
|
+
|
|
|
+当前支持:
|
|
|
+
|
|
|
+- `real`
|
|
|
+- `mock`
|
|
|
+
|
|
|
+这样 telemetry 和 HUD 不需要知道心率是从 BLE 来的还是模拟器来的。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. 玩法层当前状态
|
|
|
+
|
|
|
+### 6.1 顺序赛 classic-sequential
|
|
|
+
|
|
|
+核心特征:
|
|
|
+
|
|
|
+- 开始点先打
|
|
|
+- 打完开始点才显示完整路线
|
|
|
+- 按顺序推进控制点
|
|
|
+- 最后打终点结束
|
|
|
+
|
|
|
+地图语义:
|
|
|
+
|
|
|
+- 单目标高亮
|
|
|
+- 当前目标点闪动
|
|
|
+- 当前目标腿流动动画
|
|
|
+- 已完成点和线灰化
|
|
|
+
|
|
|
+### 6.2 积分赛 score-o
|
|
|
+
|
|
|
+核心特征:
|
|
|
+
|
|
|
+- 自由选择未完成点
|
|
|
+- 所有未收集点可见且高亮
|
|
|
+- 用户可点击某个点设为 focus
|
|
|
+- HUD 显示选中点或最近未完成点距离
|
|
|
+- 终点可随时选中并结束比赛
|
|
|
+
|
|
|
+地图语义:
|
|
|
+
|
|
|
+- 不显示顺序导航腿
|
|
|
+- 所有未完成点为多目标态
|
|
|
+- 选中点有额外强化动画
|
|
|
+- 圈内数字当前默认直接使用序号作为积分数字
|
|
|
+
|
|
|
+### 6.3 modeState 设计
|
|
|
+
|
|
|
+当前规则层已经预留并开始使用 `modeState`。
|
|
|
+
|
|
|
+含义:
|
|
|
+
|
|
|
+- 通用 `GameSessionState` 只放跨玩法共用字段
|
|
|
+- 各玩法自己的私有状态放 `modeState`
|
|
|
+
|
|
|
+这为后续新增玩法提供了稳定扩展入口。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. 当前 HUD 设计
|
|
|
+
|
|
|
+目前 HUD 有两屏:
|
|
|
+
|
|
|
+### HUD 1
|
|
|
+
|
|
|
+- 打点主信息
|
|
|
+- 用时
|
|
|
+- 里程
|
|
|
+- 当前目标距离
|
|
|
+- 当前速度
|
|
|
+- 打点进度
|
|
|
+
|
|
|
+### HUD 2
|
|
|
+
|
|
|
+- 心率
|
|
|
+- 卡路里
|
|
|
+- 平均速度
|
|
|
+- 精度
|
|
|
+- 心率区间名称与区间说明
|
|
|
+
|
|
|
+HUD 当前颜色由 telemetry 驱动。
|
|
|
+
|
|
|
+规则:
|
|
|
+
|
|
|
+- 有心率:按心率分区
|
|
|
+- 无心率:按速度代理区间 fallback
|
|
|
+
|
|
|
+当前 6 档对应:
|
|
|
+
|
|
|
+- 蓝:激活放松
|
|
|
+- 紫:动态热身
|
|
|
+- 绿:脂肪燃烧
|
|
|
+- 黄:糖分消耗
|
|
|
+- 橙:心肺训练
|
|
|
+- 红:峰值锻炼
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. 心率带体系当前设计
|
|
|
+
|
|
|
+当前心率带链路已经相对完整。
|
|
|
+
|
|
|
+### 8.1 单设备连接,多设备发现
|
|
|
+
|
|
|
+当前不是“多设备同时连接”,而是:
|
|
|
+
|
|
|
+- 扫描发现多条设备
|
|
|
+- 用户手选一条连接
|
|
|
+- 连接成功后成为首选设备
|
|
|
+- 后续自动回连只针对这条首选设备
|
|
|
+
|
|
|
+原因:
|
|
|
+
|
|
|
+- 避免误连附近别人的心率带
|
|
|
+- 保持 telemetry 和 HUD 语义简单
|
|
|
+
|
|
|
+### 8.2 首选设备持久化
|
|
|
+
|
|
|
+已实现:
|
|
|
+
|
|
|
+- 首选设备持久化到本地 storage
|
|
|
+- 重进页面后仍可识别
|
|
|
+- 自动回连优先使用首选设备
|
|
|
+
|
|
|
+### 8.3 BLE 断连重连处理
|
|
|
+
|
|
|
+这一段实现过多轮修正,目前逻辑重点是:
|
|
|
+
|
|
|
+- 手动断开与意外断开分离
|
|
|
+- 再连接前,先清理 BLE 残留连接
|
|
|
+- 必要时刷新 Bluetooth Adapter
|
|
|
+- 再重新扫描 / 建连
|
|
|
+
|
|
|
+这是目前心率带重连稳定的关键。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9. 模拟器体系当前设计
|
|
|
+
|
|
|
+### 9.1 模拟器总目标
|
|
|
+
|
|
|
+解决“GPS / 心率类 App 每次都要出去跑才能测”的问题。
|
|
|
+
|
|
|
+当前外部模拟器已经支持:
|
|
|
+
|
|
|
+- 加载 `game.json`
|
|
|
+- 加载瓦片
|
|
|
+- 加载 KML 控制点
|
|
|
+- 地图点击与拖动
|
|
|
+- 实时 GPS 发送
|
|
|
+- 路径编辑与回放
|
|
|
+- 导入轨迹文件回放
|
|
|
+- 心率模拟发送
|
|
|
+
|
|
|
+### 9.2 GPS 模拟
|
|
|
+
|
|
|
+消息协议:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "type": "mock_gps",
|
|
|
+ "timestamp": 1711267200000,
|
|
|
+ "lat": 31.2304,
|
|
|
+ "lon": 121.4737,
|
|
|
+ "accuracyMeters": 6,
|
|
|
+ "speedMps": 2.4,
|
|
|
+ "headingDeg": 135
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 9.3 心率模拟
|
|
|
+
|
|
|
+消息协议:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "type": "mock_heart_rate",
|
|
|
+ "timestamp": 1711267200000,
|
|
|
+ "bpm": 148
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 9.4 当前心率模拟能力
|
|
|
+
|
|
|
+外部模拟器当前支持:
|
|
|
+
|
|
|
+- 固定 BPM 发送
|
|
|
+- 连续发送
|
|
|
+- 六档分区样本
|
|
|
+- 真实样本模式
|
|
|
+
|
|
|
+真实样本模式又细分成:
|
|
|
+
|
|
|
+- 慢跑样本
|
|
|
+- 节奏跑样本
|
|
|
+- 间歇跑样本
|
|
|
+- 恢复走样本
|
|
|
+
|
|
|
+### 9.5 模拟器布局原则
|
|
|
+
|
|
|
+当前已经调整为:
|
|
|
+
|
|
|
+- 左侧控制面板独立滚动
|
|
|
+- 右侧地图固定不动
|
|
|
+
|
|
|
+这样更适合长面板配置和路径编辑,不会让地图区跟着滚动。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 10. 当前事件驱动模型
|
|
|
+
|
|
|
+当前系统已经较明确地进入了事件驱动模型。
|
|
|
+
|
|
|
+### 10.1 规则事件
|
|
|
+
|
|
|
+规则层的输入是 `GameEvent`。
|
|
|
+
|
|
|
+典型事件:
|
|
|
+
|
|
|
+- `session_started`
|
|
|
+- `gps_updated`
|
|
|
+- `punch_requested`
|
|
|
+- `control_focused`
|
|
|
+- `session_ended`
|
|
|
+
|
|
|
+规则层输出:
|
|
|
+
|
|
|
+- `nextState`
|
|
|
+- `presentation`
|
|
|
+- `effects`
|
|
|
+
|
|
|
+### 10.2 Telemetry 事件
|
|
|
+
|
|
|
+Telemetry 层的输入是 `TelemetryEvent`。
|
|
|
+
|
|
|
+典型事件:
|
|
|
+
|
|
|
+- `session_state_updated`
|
|
|
+- `target_updated`
|
|
|
+- `gps_updated`
|
|
|
+- `heart_rate_updated`
|
|
|
+
|
|
|
+### 10.3 反馈事件
|
|
|
+
|
|
|
+反馈层消费的是 `GameEffect[]`。
|
|
|
+
|
|
|
+这样:
|
|
|
+
|
|
|
+- 声音
|
|
|
+- 震动
|
|
|
+- UI 动效
|
|
|
+- 地图脉冲
|
|
|
+
|
|
|
+都可以走统一 effect 通道,而不是各处散写。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 11. 当前字段归属原则
|
|
|
+
|
|
|
+这是当前项目后续扩展最重要的边界之一。
|
|
|
+
|
|
|
+### 11.1 放 Telemetry
|
|
|
+
|
|
|
+适合放:
|
|
|
+
|
|
|
+- 速度
|
|
|
+- 距离
|
|
|
+- 心率
|
|
|
+- 卡路里
|
|
|
+- 精度
|
|
|
+- 当前目标距离
|
|
|
+
|
|
|
+这些是通用运行信息。
|
|
|
+
|
|
|
+### 11.2 放 GameSessionState
|
|
|
+
|
|
|
+适合放:
|
|
|
+
|
|
|
+- 当前玩法状态
|
|
|
+- 已完成点
|
|
|
+- 当前目标点
|
|
|
+- 得分
|
|
|
+- 玩法私有状态
|
|
|
+
|
|
|
+这些会影响规则推进。
|
|
|
+
|
|
|
+### 11.3 放 Presentation
|
|
|
+
|
|
|
+适合放:
|
|
|
+
|
|
|
+- HUD 文案
|
|
|
+- 当前激活腿
|
|
|
+- 哪些点高亮
|
|
|
+- 哪些点闪动
|
|
|
+- 按钮状态
|
|
|
+
|
|
|
+这些只是为了显示。
|
|
|
+
|
|
|
+后面开发新增字段时,必须先判断它属于哪层,避免再次耦合。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 12. 当前已验证成立的扩展能力
|
|
|
+
|
|
|
+当前架构已经通过以下场景验证过方向正确:
|
|
|
+
|
|
|
+1. 从单一顺序赛扩成顺序赛 + 积分赛
|
|
|
+2. 从真实 GPS 扩成真实 + 模拟 GPS
|
|
|
+3. 从真实心率带扩成真实 + 模拟心率
|
|
|
+4. 从单一 HUD 扩成双 HUD
|
|
|
+5. 从单一音效扩成统一 feedback 模型
|
|
|
+6. 从单设备心率带扩成多设备发现 + 单设备选择
|
|
|
+
|
|
|
+这说明当前架构不是只能跑一个 Demo,而是已经具备继续扩展的基础。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 13. 当前已知现实约束
|
|
|
+
|
|
|
+### 13.1 微信开发工具层级模拟不可靠
|
|
|
+
|
|
|
+目前已经确认:
|
|
|
+
|
|
|
+- 真机正常
|
|
|
+- 开发工具在 `webgl canvas + 普通 view` 上的层级模拟并不稳定
|
|
|
+
|
|
|
+因此后续验收原则是:
|
|
|
+
|
|
|
+- 真机为准
|
|
|
+- 开发工具仅做辅助观察
|
|
|
+
|
|
|
+### 13.2 小程序后台音频能力有限
|
|
|
+
|
|
|
+已经尝试过后台 guidance 音频方案,但当前阶段决定先回退:
|
|
|
+
|
|
|
+- 前台音频正常
|
|
|
+- 后台不再强做 loop guidance
|
|
|
+
|
|
|
+后续如果重新开启,需要:
|
|
|
+
|
|
|
+- 更适合循环的音频素材
|
|
|
+- 更稳定的后台音频策略
|
|
|
+
|
|
|
+### 13.3 BLE 生命周期比 JS 状态更慢
|
|
|
+
|
|
|
+心率带重连问题已经证明:
|
|
|
+
|
|
|
+- 逻辑没错,不等于 BLE 底层状态已释放
|
|
|
+
|
|
|
+所以后续涉及 BLE 时,必须继续保留:
|
|
|
+
|
|
|
+- 清场
|
|
|
+- 延迟
|
|
|
+- 状态同步
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 14. 当前阶段的结论
|
|
|
+
|
|
|
+到目前为止,这个项目已经完成了从“功能堆叠”到“可扩展结构”的第一阶段转变。
|
|
|
+
|
|
|
+现阶段最重要的成果不是某一个按钮或某一个玩法,而是以下架构能力已经成立:
|
|
|
+
|
|
|
+- 地图引擎与规则解耦
|
|
|
+- 规则事件驱动
|
|
|
+- telemetry 独立成层
|
|
|
+- presentation 拆成 map/hud
|
|
|
+- feedback 统一消费 effects
|
|
|
+- 真实 / 模拟传感器源可插拔
|
|
|
+- 外部模拟器可驱动整条业务链
|
|
|
+
|
|
|
+这意味着后续继续开发时:
|
|
|
+
|
|
|
+- 新玩法主要加 rule plugin
|
|
|
+- 新通用统计主要加 telemetry
|
|
|
+- 新反馈主要加 effect 消费端
|
|
|
+- 新传感器 / 模拟器主要加 sensor source
|
|
|
+
|
|
|
+而不应该再把所有逻辑堆回 `MapEngine` 或页面里。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 15. 后续建议
|
|
|
+
|
|
|
+当前阶段之后,建议按以下优先级推进:
|
|
|
+
|
|
|
+1. 继续增加玩法插件
|
|
|
+2. 增强调试面板与模拟器联调能力
|
|
|
+3. 给更多玩法留 `modeState` 专属状态
|
|
|
+4. 逐步丰富 telemetry 项
|
|
|
+5. 继续打磨地图引擎的路线渲染能力
|
|
|
+
|
|
|
+但无论怎么扩,建议始终遵守本文档里的边界原则。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 16. 新玩法扩展流程
|
|
|
+
|
|
|
+后续新增玩法时,建议始终按下面这条路径落地,而不是直接往 `MapEngine` 或页面里塞逻辑。
|
|
|
+
|
|
|
+```mermaid
|
|
|
+flowchart TD
|
|
|
+ A["新增玩法需求"] --> B["确定规则目标<br/>完成条件 / 得分 / 失败条件 / 解锁逻辑"]
|
|
|
+ B --> C["定义 modeState<br/>只放玩法私有状态"]
|
|
|
+ C --> D["新增 RulePlugin<br/>reduce + buildPresentation"]
|
|
|
+ D --> E["补充 GameDefinition 配置项<br/>让远程配置可描述此玩法"]
|
|
|
+ E --> F["拆 presentation<br/>map / hud 分别表达"]
|
|
|
+ F --> G{"是否需要新通用统计?"}
|
|
|
+ G -- "是" --> H["扩 TelemetryRuntime<br/>仅加入跨玩法可复用信息"]
|
|
|
+ G -- "否" --> I["保持 telemetry 不动"]
|
|
|
+ H --> J{"是否需要新反馈?"}
|
|
|
+ I --> J
|
|
|
+ J -- "是" --> K["新增 GameEffect 消费端<br/>声音 / 震动 / UI / 地图特效"]
|
|
|
+ J -- "否" --> L["保持 feedback 不动"]
|
|
|
+ K --> M["调试面板补入口<br/>只做该层对应的调试能力"]
|
|
|
+ L --> M
|
|
|
+ M --> N["外部模拟器补样本<br/>仅在确有联调价值时添加"]
|
|
|
+ N --> O["真机联调与验收"]
|
|
|
+```
|
|
|
+
|
|
|
+### 16.1 新玩法落地时的边界检查
|
|
|
+
|
|
|
+新增玩法前,建议先问下面几个问题:
|
|
|
+
|
|
|
+1. 这是规则状态,还是通用信息?
|
|
|
+ - 规则状态放 `modeState`
|
|
|
+ - 通用信息放 `telemetry`
|
|
|
+
|
|
|
+2. 这是地图显示语义,还是 HUD 文案语义?
|
|
|
+ - 地图相关放 `map presentation`
|
|
|
+ - HUD 相关放 `hud presentation`
|
|
|
+
|
|
|
+3. 这是玩法逻辑,还是地图能力?
|
|
|
+ - 玩法逻辑放 rule plugin
|
|
|
+ - 地图能力放 engine / renderer
|
|
|
+
|
|
|
+4. 这是单玩法专属能力,还是跨玩法复用能力?
|
|
|
+ - 专属能力优先局部实现
|
|
|
+ - 复用能力再提升为全局层
|
|
|
+
|
|
|
+### 16.2 建议的新增玩法最小模板
|
|
|
+
|
|
|
+后续新增一个玩法时,建议至少补这些文件或模块:
|
|
|
+
|
|
|
+- `game/rules/<newMode>Rule.ts`
|
|
|
+- `game/content/courseToGameDefinition.ts` 中对应模式的定义装配
|
|
|
+- `game/core/gameDefinition.ts` 中新增 mode 支持
|
|
|
+- `game/presentation/*` 中新增该玩法需要的 map/hud 字段
|
|
|
+- `pages/map/map.wxml` 只在确有显示需求时接新 HUD 文案
|
|
|
+- 调试面板里补最小测试入口
|
|
|
+
|
|
|
+如果一个新玩法一上来就需要大改:
|
|
|
+
|
|
|
+- `MapEngine`
|
|
|
+- `TelemetryRuntime`
|
|
|
+- `Renderer`
|
|
|
+
|
|
|
+那通常说明这次设计边界还没想清楚,应该先回头重审玩法抽象。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 17. 关键文件索引
|
|
|
+
|
|
|
+### 地图与宿主
|
|
|
+
|
|
|
+- [mapEngine.ts](D:/dev/cmr-mini/miniprogram/engine/map/mapEngine.ts)
|
|
|
+- [map.ts](D:/dev/cmr-mini/miniprogram/pages/map/map.ts)
|
|
|
+- [map.wxml](D:/dev/cmr-mini/miniprogram/pages/map/map.wxml)
|
|
|
+
|
|
|
+### 规则层
|
|
|
+
|
|
|
+- [gameRuntime.ts](D:/dev/cmr-mini/miniprogram/game/core/gameRuntime.ts)
|
|
|
+- [classicSequentialRule.ts](D:/dev/cmr-mini/miniprogram/game/rules/classicSequentialRule.ts)
|
|
|
+- [scoreORule.ts](D:/dev/cmr-mini/miniprogram/game/rules/scoreORule.ts)
|
|
|
+
|
|
|
+### Presentation
|
|
|
+
|
|
|
+- [presentationState.ts](D:/dev/cmr-mini/miniprogram/game/presentation/presentationState.ts)
|
|
|
+- [mapPresentationState.ts](D:/dev/cmr-mini/miniprogram/game/presentation/mapPresentationState.ts)
|
|
|
+- [hudPresentationState.ts](D:/dev/cmr-mini/miniprogram/game/presentation/hudPresentationState.ts)
|
|
|
+
|
|
|
+### Telemetry
|
|
|
+
|
|
|
+- [telemetryRuntime.ts](D:/dev/cmr-mini/miniprogram/game/telemetry/telemetryRuntime.ts)
|
|
|
+- [telemetryConfig.ts](D:/dev/cmr-mini/miniprogram/game/telemetry/telemetryConfig.ts)
|
|
|
+
|
|
|
+### Feedback
|
|
|
+
|
|
|
+- [feedbackDirector.ts](D:/dev/cmr-mini/miniprogram/game/feedback/feedbackDirector.ts)
|
|
|
+- [soundDirector.ts](D:/dev/cmr-mini/miniprogram/game/audio/soundDirector.ts)
|
|
|
+
|
|
|
+### 传感层
|
|
|
+
|
|
|
+- [locationController.ts](D:/dev/cmr-mini/miniprogram/engine/sensor/locationController.ts)
|
|
|
+- [heartRateController.ts](D:/dev/cmr-mini/miniprogram/engine/sensor/heartRateController.ts)
|
|
|
+- [heartRateInputController.ts](D:/dev/cmr-mini/miniprogram/engine/sensor/heartRateInputController.ts)
|
|
|
+- [mockLocationBridge.ts](D:/dev/cmr-mini/miniprogram/engine/sensor/mockLocationBridge.ts)
|
|
|
+- [mockHeartRateBridge.ts](D:/dev/cmr-mini/miniprogram/engine/sensor/mockHeartRateBridge.ts)
|
|
|
+
|
|
|
+### 外部模拟器
|
|
|
+
|
|
|
+- [server.js](D:/dev/cmr-mini/tools/mock-gps-sim/server.js)
|
|
|
+- [index.html](D:/dev/cmr-mini/tools/mock-gps-sim/public/index.html)
|
|
|
+- [simulator.js](D:/dev/cmr-mini/tools/mock-gps-sim/public/simulator.js)
|
|
|
+- [style.css](D:/dev/cmr-mini/tools/mock-gps-sim/public/style.css)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 18. 调试与模拟体系
|
|
|
+
|
|
|
+当前项目已经不再依赖“必须出门跑一遍”才能测通主流程。
|
|
|
+围绕调试和联调,已经形成了两条互补链路:
|
|
|
+
|
|
|
+- 小程序内调试面板
|
|
|
+- 外部模拟器
|
|
|
+
|
|
|
+二者是互补关系,不是替代关系。
|
|
|
+
|
|
|
+### 18.1 调试体系关系图
|
|
|
+
|
|
|
+```mermaid
|
|
|
+flowchart LR
|
|
|
+ DEV["开发者"] --> MP["小程序调试面板"]
|
|
|
+ DEV --> SIM["外部模拟器"]
|
|
|
+
|
|
|
+ MP --> MGPS["切换 mock GPS"]
|
|
|
+ MP --> MHR["切换 mock 心率"]
|
|
|
+ MP --> MODE["切换玩法 / 调试动作"]
|
|
|
+
|
|
|
+ SIM --> WS["WebSocket 桥"]
|
|
|
+ WS --> GPSMSG["mock_gps"]
|
|
|
+ WS --> HRMSG["mock_heart_rate"]
|
|
|
+
|
|
|
+ GPSMSG --> LC["LocationController"]
|
|
|
+ HRMSG --> HRC["HeartRateInputController"]
|
|
|
+
|
|
|
+ LC --> ME["MapEngine"]
|
|
|
+ HRC --> ME
|
|
|
+ MODE --> ME
|
|
|
+
|
|
|
+ ME --> GR["GameRuntime"]
|
|
|
+ ME --> TR["TelemetryRuntime"]
|
|
|
+ GR --> HUD["HUD / 地图渲染"]
|
|
|
+ TR --> HUD
|
|
|
+
|
|
|
+ DEV --> REAL["真机联调"]
|
|
|
+ REAL --> BLE["真实心率带"]
|
|
|
+ REAL --> GPS["真实 GPS"]
|
|
|
+ BLE --> HRC
|
|
|
+ GPS --> LC
|
|
|
+```
|
|
|
+
|
|
|
+### 18.2 小程序调试面板的职责
|
|
|
+
|
|
|
+调试面板位于 [map.wxml](D:/dev/cmr-mini/miniprogram/pages/map/map.wxml) 和 [map.ts](D:/dev/cmr-mini/miniprogram/pages/map/map.ts)。
|
|
|
+
|
|
|
+它的职责是:
|
|
|
+
|
|
|
+- 查看当前局状态
|
|
|
+- 查看 GPS / 心率 / 罗盘状态
|
|
|
+- 切换真实源 / 模拟源
|
|
|
+- 触发局部调试动作
|
|
|
+- 展示设备列表与连接状态
|
|
|
+- 辅助验证 HUD 和地图渲染状态
|
|
|
+
|
|
|
+调试面板的原则是:
|
|
|
+
|
|
|
+- 只操作已经存在的系统能力
|
|
|
+- 不直接篡改规则内部状态
|
|
|
+- 调试动作尽量对应单一层
|
|
|
+
|
|
|
+也就是说:
|
|
|
+
|
|
|
+- 连接心率带,属于 sensor 调试
|
|
|
+- 切换玩法,属于 rule 调试
|
|
|
+- 切 mock bridge,属于传感输入源调试
|
|
|
+- 心率分区测试,属于 telemetry 调试
|
|
|
+
|
|
|
+### 18.3 外部模拟器的职责
|
|
|
+
|
|
|
+外部模拟器位于 [tools/mock-gps-sim](D:/dev/cmr-mini/tools/mock-gps-sim)。
|
|
|
+
|
|
|
+它的职责是:
|
|
|
+
|
|
|
+- 室内模拟 GPS 位置流
|
|
|
+- 模拟路径回放
|
|
|
+- 模拟心率值和心率曲线
|
|
|
+- 复用真实 `game.json / KML / tiles`
|
|
|
+- 在电脑侧快速联调地图、HUD、规则
|
|
|
+
|
|
|
+当前它已经支持:
|
|
|
+
|
|
|
+- 载入 `game.json`
|
|
|
+- 载入 KML 控制点
|
|
|
+- 载入瓦片模板
|
|
|
+- 实时 GPS 发送
|
|
|
+- 路径编辑
|
|
|
+- 路径回放
|
|
|
+- 导入轨迹文件
|
|
|
+- 固定心率发送
|
|
|
+- 分区样本心率发送
|
|
|
+- 真实样本心率模板发送
|
|
|
+
|
|
|
+### 18.4 当前 mock 通信协议
|
|
|
+
|
|
|
+GPS:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "type": "mock_gps",
|
|
|
+ "timestamp": 1711267200000,
|
|
|
+ "lat": 31.2304,
|
|
|
+ "lon": 121.4737,
|
|
|
+ "accuracyMeters": 6,
|
|
|
+ "speedMps": 2.4,
|
|
|
+ "headingDeg": 135
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+心率:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "type": "mock_heart_rate",
|
|
|
+ "timestamp": 1711267200000,
|
|
|
+ "bpm": 148
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+两者当前共用同一个 WebSocket 入口:
|
|
|
+
|
|
|
+- `.../mock-gps`
|
|
|
+
|
|
|
+这是当前阶段为了降低复杂度做的统一通道设计,后面如果模拟消息种类继续增加,再考虑独立通道或消息总线拆分。
|
|
|
+
|
|
|
+### 18.5 当前推荐的联调顺序
|
|
|
+
|
|
|
+如果要联调一个完整玩法,建议按这个顺序:
|
|
|
+
|
|
|
+1. 先载入配置与控制点
|
|
|
+2. 小程序切到 mock GPS / mock heart rate
|
|
|
+3. 外部模拟器连接桥接
|
|
|
+4. 先用固定值测试最小闭环
|
|
|
+5. 再切路径回放和心率样本
|
|
|
+6. 最后再上真机 + 真实 GPS / BLE 心率带验收
|
|
|
+
|
|
|
+这个顺序的好处是:
|
|
|
+
|
|
|
+- 先验证链路
|
|
|
+- 再验证动态过程
|
|
|
+- 最后再验证真实设备行为
|
|
|
+
|
|
|
+### 18.6 为什么调试体系很重要
|
|
|
+
|
|
|
+对这类 GPS / 心率 / 定向玩法类项目来说,最大的开发瓶颈往往不是代码本身,而是:
|
|
|
+
|
|
|
+- 需要空间移动
|
|
|
+- 需要真实设备
|
|
|
+- 需要时间成本
|
|
|
+- 需要复现复杂路径
|
|
|
+
|
|
|
+因此调试与模拟体系本身就是底座能力的一部分,而不是临时工具。
|
|
|
+
|
|
|
+后续建议继续把以下能力都优先放在这条体系里:
|
|
|
+
|
|
|
+- 轨迹回放模板
|
|
|
+- 心率样本模板
|
|
|
+- 特殊玩法样本
|
|
|
+- 多阶段规则验证入口
|
|
|
+- 关键状态可视化
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+本文档用于当前阶段开发总结。
|
|
|
+后续如果架构继续升级,建议直接在本文件上持续迭代,而不是另起多份架构说明,避免信息分散。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 19. 当前开发约定
|
|
|
+
|
|
|
+为了避免后续开发过程中重新把边界做乱,当前阶段建议固定以下约定。
|
|
|
+
|
|
|
+### 19.1 修改代码前先判断归属层
|
|
|
+
|
|
|
+新增需求时,先判断它属于哪一层,再落代码:
|
|
|
+
|
|
|
+- 地图能力问题:优先进 `engine`
|
|
|
+- 玩法判定问题:优先进 `game/rules`
|
|
|
+- 通用运动信息问题:优先进 `game/telemetry`
|
|
|
+- 纯展示文案或 HUD 结构问题:优先进 `game/presentation` 或页面层
|
|
|
+- 声音、震动、地图脉冲等反馈问题:优先进 `game/feedback`
|
|
|
+- 调试动作与仿真工具问题:优先进调试面板或外部模拟器
|
|
|
+
|
|
|
+如果一个需求同时改动太多层,通常说明边界还没想清楚。
|
|
|
+
|
|
|
+### 19.2 先扩事件,再扩功能
|
|
|
+
|
|
|
+当前架构是事件驱动主链,因此新增能力时优先顺序建议为:
|
|
|
+
|
|
|
+1. 先定义输入事件
|
|
|
+2. 再定义规则输出的 `effect` 或 `presentation`
|
|
|
+3. 最后补具体的 UI / 声音 / 动效消费端
|
|
|
+
|
|
|
+不要直接在页面里写死业务副作用,也不要让渲染器自己猜业务状态。
|
|
|
+
|
|
|
+### 19.3 build 版本号约定
|
|
|
+
|
|
|
+当前小程序页面 build 号统一写在:
|
|
|
+
|
|
|
+- [map.ts](D:/dev/cmr-mini/miniprogram/pages/map/map.ts)
|
|
|
+
|
|
|
+约定为:
|
|
|
+
|
|
|
+- 每次发生用户可感知的页面 / 地图 / 调试 / 玩法改动时,自增 1
|
|
|
+- 只做文档或纯注释修改时,可以不变
|
|
|
+
|
|
|
+这样方便现场确认当前安装包和工作副本是否一致。
|
|
|
+
|
|
|
+### 19.4 提交约定
|
|
|
+
|
|
|
+当前开发分支约定使用:
|
|
|
+
|
|
|
+- `codex/*`
|
|
|
+
|
|
|
+提交时建议遵守:
|
|
|
+
|
|
|
+- 一次提交只围绕一个相对完整的目标
|
|
|
+- 不把开发工具噪声配置混进业务提交
|
|
|
+- 优先提交可运行的阶段闭环,而不是半成品
|
|
|
+
|
|
|
+当前项目里一个典型例子是:
|
|
|
+
|
|
|
+- [project.config.json](D:/dev/cmr-mini/project.config.json)
|
|
|
+
|
|
|
+这类本地开发工具配置,不应该默认混入功能提交。
|
|
|
+
|
|
|
+### 19.5 自测约定
|
|
|
+
|
|
|
+当前阶段每次改动后的最低自测要求建议是:
|
|
|
+
|
|
|
+- 代码改动后执行 `npm run typecheck`
|
|
|
+- 外部模拟器脚本改动后,额外执行 `node --check`
|
|
|
+- 涉及地图 / HUD / 层级表现的问题,以真机为准
|
|
|
+- 涉及 BLE / GPS / 后台行为的问题,必须至少走一遍真机联调
|
|
|
+
|
|
|
+原因是:
|
|
|
+
|
|
|
+- 微信开发工具对 `webgl canvas`、原生层级、蓝牙、后台音频的模拟都不完全可靠
|
|
|
+
|
|
|
+### 19.6 当前阶段已知现实约束
|
|
|
+
|
|
|
+这些不是 bug,但开发时要心里有数:
|
|
|
+
|
|
|
+- 微信开发工具里,`webgl canvas` 与普通视图层级表现可能和真机不一致
|
|
|
+- 后台音频能力当前不稳定,因此已经回退为前台策略
|
|
|
+- BLE 心率带连接存在底层资源释放延迟,重连逻辑必须保守处理
|
|
|
+- 模拟器与真机联调时,公网 / 局域网 WebSocket 地址要明确区分
|
|
|
+
|
|
|
+### 19.7 当前阶段最重要的判断标准
|
|
|
+
|
|
|
+如果新增一个功能后出现以下现象,就要停下来重新审视设计:
|
|
|
+
|
|
|
+- 为了做一个玩法,不得不大改 `MapEngine`
|
|
|
+- 为了做一个 HUD 文案,不得不改 `RuleEngine`
|
|
|
+- 为了做一个声音效果,不得不改地图渲染逻辑
|
|
|
+- 为了做一个模拟输入,不得不绕过传感层直接写 telemetry
|
|
|
+
|
|
|
+出现这些情况,通常说明实现绕过了当前架构,应优先回到分层原则重新整理。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 20. 后续演进路线图
|
|
|
+
|
|
|
+当前架构已经从“能跑”进入“可持续扩展”阶段。
|
|
|
+后续建议不要无序加功能,而是按时间层次推进。
|
|
|
+
|
|
|
+### 20.1 近期目标
|
|
|
+
|
|
|
+近期目标的重点不是再造新架构,而是继续把现在这套底座打磨稳。
|
|
|
+
|
|
|
+建议优先推进:
|
|
|
+
|
|
|
+- 继续完善顺序赛与积分赛的细节体验
|
|
|
+- 继续扩充调试面板,但保持分组清晰
|
|
|
+- 继续扩充外部模拟器,让常见流程都能室内复现
|
|
|
+- 持续清理地图引擎与规则层之间可能重新耦合的点
|
|
|
+- 对关键链路补更多真机验证经验
|
|
|
+
|
|
|
+这个阶段的目标是:
|
|
|
+
|
|
|
+- 日常开发大部分功能都能在室内完成联调
|
|
|
+- 新玩法加入时不需要再反复返工底层分层
|
|
|
+
|
|
|
+### 20.2 中期目标
|
|
|
+
|
|
|
+中期更适合开始扩充玩法族,而不是继续只做当前两种玩法。
|
|
|
+
|
|
|
+建议方向:
|
|
|
+
|
|
|
+- 增加更多 `RulePlugin`
|
|
|
+- 开始让 `modeState` 真正承载玩法私有状态
|
|
|
+- 继续把玩法专属 HUD / 地图表现做成独立 adapter
|
|
|
+- 把玩法配置进一步从“少量字段”扩成更完整的 `game` 段
|
|
|
+- 让反馈系统支持更清晰的事件 profile
|
|
|
+
|
|
|
+这个阶段的核心判断标准是:
|
|
|
+
|
|
|
+- 新玩法主要新增规则与 presentation 文件
|
|
|
+- 而不是大改已有底层模块
|
|
|
+
|
|
|
+### 20.3 远期目标
|
|
|
+
|
|
|
+远期不建议先追求花哨表现,而是继续把底座能力做成可复用资产。
|
|
|
+
|
|
|
+可能的方向包括:
|
|
|
+
|
|
|
+- 玩法模板化
|
|
|
+- 外部模拟器支持更多传感器类型
|
|
|
+- 赛后回放与事件日志分析
|
|
|
+- 多人 / 团队玩法
|
|
|
+- 更完整的内容配置系统
|
|
|
+- 更稳定的后台能力方案
|
|
|
+
|
|
|
+到这个阶段,理想状态是:
|
|
|
+
|
|
|
+- 这套底座不只服务当前项目
|
|
|
+- 也能被后续其它运动地图类 App 直接复用
|
|
|
+
|
|
|
+### 20.4 当前最值得持续投入的底座能力
|
|
|
+
|
|
|
+如果要挑几项最值得长期投入的底座能力,当前建议是:
|
|
|
+
|
|
|
+- `RulePlugin` 扩展能力
|
|
|
+- `Telemetry` 的稳定性和通用性
|
|
|
+- 外部模拟器
|
|
|
+- 调试面板
|
|
|
+- 地图路线符号系统
|
|
|
+- 反馈事件体系
|
|
|
+
|
|
|
+这些能力的共同价值在于:
|
|
|
+
|
|
|
+- 一次投入,可以被多个玩法复用
|
|
|
+
|
|
|
+### 20.5 当前不建议过早投入的方向
|
|
|
+
|
|
|
+以下方向并不是不做,而是不建议当前阶段优先投入:
|
|
|
+
|
|
|
+- 为了开发工具显示偏差而重构正式页面层级
|
|
|
+- 一上来做复杂多人同步
|
|
|
+- 一上来做过重的后台音频方案
|
|
|
+- 在还没有足够玩法前就过度抽象成庞大平台
|
|
|
+
|
|
|
+当前更重要的是:
|
|
|
+
|
|
|
+- 把已经证明有效的主链打磨稳
|
|
|
+- 再逐步扩展玩法和能力
|