整理中文文档结构与索引
This commit is contained in:
164
doc/archive/animation/动画接入规格模板.md
Normal file
164
doc/archive/animation/动画接入规格模板.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# 动画接入规格模板
|
||||
|
||||
## 1. 用途
|
||||
|
||||
这份模板用于:
|
||||
|
||||
- 设计公司交付动画时填写
|
||||
- 开发接入前确认规格
|
||||
- 作为动画实现与验收依据
|
||||
|
||||
建议:一个动画一条记录。
|
||||
|
||||
---
|
||||
|
||||
## 2. 基础信息模板
|
||||
|
||||
```md
|
||||
动画 ID:
|
||||
动画名称:
|
||||
所属页面/模块:
|
||||
所属层级:地图空间 / HUD / UI反馈 / 过场
|
||||
优先级:高 / 中 / 低
|
||||
|
||||
触发事件:
|
||||
触发条件:
|
||||
是否高频:
|
||||
|
||||
作用对象:
|
||||
起始状态:
|
||||
结束状态:
|
||||
|
||||
动画形式:程序动画 / Lottie / 序列帧 / 视频 / 其他
|
||||
资源文件:
|
||||
|
||||
时长:
|
||||
延迟:
|
||||
缓动:
|
||||
是否循环:
|
||||
是否可中断:
|
||||
重复触发策略:覆盖 / 忽略 / 重启 / 排队
|
||||
|
||||
standard 表现:
|
||||
lite 表现:
|
||||
是否允许关闭:
|
||||
|
||||
设计说明:
|
||||
开发备注:
|
||||
验收标准:
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 字段说明
|
||||
|
||||
### 动画 ID
|
||||
|
||||
要求:
|
||||
|
||||
- 全局唯一
|
||||
- 使用英文或稳定标识
|
||||
|
||||
例如:
|
||||
|
||||
- `control_complete_flash`
|
||||
- `target_ready_pulse`
|
||||
- `session_intro_banner`
|
||||
|
||||
### 所属层级
|
||||
|
||||
建议四选一:
|
||||
|
||||
- 地图空间
|
||||
- HUD
|
||||
- UI反馈
|
||||
- 过场
|
||||
|
||||
### 触发事件
|
||||
|
||||
必须明确写出触发它的事件。
|
||||
|
||||
例如:
|
||||
|
||||
- `control_completed:control`
|
||||
- `control_skipped`
|
||||
- `session_started`
|
||||
- `heart_rate_zone_changed:red`
|
||||
|
||||
### 重复触发策略
|
||||
|
||||
必须提前约定:
|
||||
|
||||
- `覆盖`
|
||||
- `忽略`
|
||||
- `重启`
|
||||
- `排队`
|
||||
|
||||
否则高频触发时容易行为不一致。
|
||||
|
||||
### standard / lite
|
||||
|
||||
每个动画都必须给出两档建议。
|
||||
|
||||
例如:
|
||||
|
||||
- `standard`:完整 pulse + 外环
|
||||
- `lite`:保留单层 pulse,去掉外环
|
||||
|
||||
---
|
||||
|
||||
## 4. 推荐填写示例
|
||||
|
||||
```md
|
||||
动画 ID:control_complete_flash
|
||||
动画名称:打点成功轻闪光
|
||||
所属页面/模块:地图页
|
||||
所属层级:UI反馈
|
||||
优先级:高
|
||||
|
||||
触发事件:control_completed:control
|
||||
触发条件:普通检查点打点成功
|
||||
是否高频:是
|
||||
|
||||
作用对象:地图主舞台
|
||||
起始状态:正常地图状态
|
||||
结束状态:恢复正常地图状态
|
||||
|
||||
动画形式:程序动画
|
||||
资源文件:无
|
||||
|
||||
时长:320ms
|
||||
延迟:0ms
|
||||
缓动:ease-out
|
||||
是否循环:否
|
||||
是否可中断:是
|
||||
重复触发策略:重启
|
||||
|
||||
standard 表现:淡白色局部 flash,透明度较明显
|
||||
lite 表现:透明度降低 50%,时长缩短到 220ms
|
||||
是否允许关闭:是
|
||||
|
||||
设计说明:突出“完成打点”的即时成功感
|
||||
开发备注:通过 UiEffectDirector 下发 stageFx
|
||||
验收标准:普通打点时稳定触发,连续打点不拖尾
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 对设计公司的要求
|
||||
|
||||
建议以后明确告诉设计公司:
|
||||
|
||||
- 不只要演示稿
|
||||
- 必须提供参数规格
|
||||
- 必须提供资源清单
|
||||
- 必须说明低配降级方案
|
||||
|
||||
---
|
||||
|
||||
## 6. 结论
|
||||
|
||||
这份模板的目的是把动画从“视觉稿”变成“工程规格”。
|
||||
只有规格明确,程序才能稳定接入。
|
||||
|
||||
|
||||
163
doc/archive/animation/动画接入评审清单.md
Normal file
163
doc/archive/animation/动画接入评审清单.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# 动画接入评审清单
|
||||
|
||||
## 1. 用途
|
||||
|
||||
这份清单用于在接设计稿或准备开发前,快速判断:
|
||||
|
||||
- 这个动画能不能接
|
||||
- 应该接到哪一层
|
||||
- 有没有性能风险
|
||||
- 有没有交付缺口
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计稿评审
|
||||
|
||||
### 2.1 动画目标是否明确
|
||||
|
||||
- 这个动画是为了表达什么?
|
||||
- 它是状态反馈,还是纯装饰?
|
||||
- 用户不看它,会不会影响理解?
|
||||
|
||||
### 2.2 触发条件是否明确
|
||||
|
||||
- 由哪个事件触发?
|
||||
- 是否会高频触发?
|
||||
- 是否允许重复触发?
|
||||
|
||||
### 2.3 交付是否完整
|
||||
|
||||
- 是否有参数规格?
|
||||
- 是否有资源文件?
|
||||
- 是否有尺寸 / 比例说明?
|
||||
- 是否有 `lite` 降级说明?
|
||||
|
||||
---
|
||||
|
||||
## 3. 技术评审
|
||||
|
||||
### 3.1 该动画属于哪一层
|
||||
|
||||
- 地图空间
|
||||
- HUD
|
||||
- UI反馈
|
||||
- 过场
|
||||
|
||||
### 3.2 最合适的实现方式是什么
|
||||
|
||||
- 程序动画
|
||||
- Lottie
|
||||
- 序列帧
|
||||
- 视频
|
||||
|
||||
### 3.3 是否真的需要资源文件
|
||||
|
||||
很多动画其实可以纯程序实现,不需要额外资源。
|
||||
|
||||
如果只是:
|
||||
|
||||
- pulse
|
||||
- 渐隐
|
||||
- 平移
|
||||
- 数字过渡
|
||||
|
||||
优先用程序动画。
|
||||
|
||||
---
|
||||
|
||||
## 4. 性能评审
|
||||
|
||||
### 4.1 是否高频
|
||||
|
||||
如果是高频事件,不适合做重动画:
|
||||
|
||||
- GPS 更新
|
||||
- compass heading 更新
|
||||
- 拖动 / 缩放
|
||||
- telemetry 微小变化
|
||||
|
||||
### 4.2 lite 模式怎么处理
|
||||
|
||||
必须明确:
|
||||
|
||||
- 保留
|
||||
- 简化
|
||||
- 关闭
|
||||
|
||||
### 4.3 是否会增加页面层负担
|
||||
|
||||
要判断:
|
||||
|
||||
- 会不会引入高频 `setData`
|
||||
- 会不会创建大数组
|
||||
- 会不会增加持续循环动画
|
||||
- 会不会增加桥接成本
|
||||
|
||||
---
|
||||
|
||||
## 5. 交互评审
|
||||
|
||||
### 5.1 是否可中断
|
||||
|
||||
- 用户切页面怎么办?
|
||||
- 状态瞬间变化怎么办?
|
||||
- 连续触发怎么办?
|
||||
|
||||
### 5.2 是否会和现有动画冲突
|
||||
|
||||
- 同一事件是否已有动画?
|
||||
- 是否会重复表达同一个信息?
|
||||
- 是否和现有地图 pulse / HUD 动效叠加过重?
|
||||
|
||||
---
|
||||
|
||||
## 6. 当前项目特别注意项
|
||||
|
||||
### 6.1 地图过程中的动画必须克制
|
||||
|
||||
因为当前项目:
|
||||
|
||||
- 地图是主舞台
|
||||
- 低端机性能敏感
|
||||
- 页面桥接成本高
|
||||
|
||||
所以:
|
||||
|
||||
- 地图上的高频动画必须轻量
|
||||
- 尽量减少页面层大范围动画
|
||||
|
||||
### 6.2 优先动画化高价值节点
|
||||
|
||||
优先做:
|
||||
|
||||
- 打点成功
|
||||
- 目标切换
|
||||
- 跳点
|
||||
- 危险 / 高压反馈
|
||||
|
||||
延后做:
|
||||
|
||||
- 纯装饰性微动效
|
||||
- 复杂长演出
|
||||
|
||||
---
|
||||
|
||||
## 7. 验收清单
|
||||
|
||||
动画接入完成后,至少确认:
|
||||
|
||||
- 触发时机正确
|
||||
- 结束时机正确
|
||||
- 多次连续触发稳定
|
||||
- `standard / lite` 都可用
|
||||
- 低端机可接受
|
||||
- 不破坏现有状态链
|
||||
|
||||
---
|
||||
|
||||
## 8. 结论
|
||||
|
||||
动画接入前,只要这份清单里有明显回答不出来的问题,就不应该直接开做。
|
||||
先补规格,再接程序。
|
||||
|
||||
|
||||
451
doc/archive/animation/动画设计方案.md
Normal file
451
doc/archive/animation/动画设计方案.md
Normal file
@@ -0,0 +1,451 @@
|
||||
# 动效系统设计方案
|
||||
|
||||
本文档用于整理当前项目后续的动画 / 动效建设方案,目标不是单纯“让界面更花”,而是把动画正式纳入现有架构,成为:
|
||||
|
||||
- 状态感知工具
|
||||
- 空间注意力引导工具
|
||||
- 操作反馈工具
|
||||
- 节奏增强工具
|
||||
|
||||
当前系统已经具备:
|
||||
|
||||
- 地图引擎
|
||||
- 规则引擎
|
||||
- telemetry
|
||||
- presentation
|
||||
- feedback
|
||||
|
||||
因此动画系统最合理的做法,不是零散补丁,而是按层管理、按事件驱动、按配置扩展。
|
||||
|
||||
---
|
||||
|
||||
## 1. 设计原则
|
||||
|
||||
后续动画建设建议遵循以下原则:
|
||||
|
||||
### 1.1 动画服务于玩法,不只是装饰
|
||||
|
||||
动画优先回答这些问题:
|
||||
|
||||
- 现在发生了什么
|
||||
- 用户该看哪里
|
||||
- 这次操作是否成功
|
||||
- 当前节奏是在紧张、平稳还是危险
|
||||
|
||||
### 1.2 动画要分层
|
||||
|
||||
不要把所有动画都堆在页面层的 class 切换里。
|
||||
后续应按:
|
||||
|
||||
- 地图空间动画
|
||||
- HUD 动画
|
||||
- 反馈动画
|
||||
- 页面微交互动画
|
||||
|
||||
分层管理。
|
||||
|
||||
### 1.3 动画要和事件绑定
|
||||
|
||||
动画应该由事件或状态变化触发,而不是页面自己猜。
|
||||
|
||||
例如:
|
||||
|
||||
- `control_completed`
|
||||
- `control_skipped`
|
||||
- `guidance_state_changed`
|
||||
- `session_started`
|
||||
- `session_finished`
|
||||
- `heart_rate_zone_changed`
|
||||
- `gps_lock_changed`
|
||||
|
||||
### 1.4 动画要支持降级
|
||||
|
||||
低端机和正式版都需要降级策略。
|
||||
后续建议统一支持:
|
||||
|
||||
- `animationsEnabled`
|
||||
- `animationLevel = low / medium / high`
|
||||
|
||||
---
|
||||
|
||||
## 2. 动画分层方案
|
||||
|
||||
## 2.1 地图空间动画
|
||||
|
||||
这一层最重要,也最贴玩法。
|
||||
|
||||
适合放在:
|
||||
|
||||
- `MapPresentation`
|
||||
- `MapScene`
|
||||
- `WebGL renderer`
|
||||
|
||||
典型内容:
|
||||
|
||||
- 当前目标点脉冲
|
||||
- 可打点状态强化
|
||||
- 已完成点过渡
|
||||
- 已跳过点灰态过渡
|
||||
- 地图 pulse
|
||||
- 危险区呼吸
|
||||
- 迷雾 reveal 扩散
|
||||
- 金币收集爆点
|
||||
- 幽灵感知圈变化
|
||||
|
||||
### 这一层的特点
|
||||
|
||||
- 与地图空间对象绑定
|
||||
- 最不适合用 WXML 硬拼
|
||||
- 应由渲染层持续驱动
|
||||
|
||||
---
|
||||
|
||||
## 2.2 HUD 动画
|
||||
|
||||
这一层用于数值和状态提示,不直接改地图对象。
|
||||
|
||||
适合放在:
|
||||
|
||||
- 页面层
|
||||
- HUD 组件层
|
||||
|
||||
典型内容:
|
||||
|
||||
- 目标距离数字滑变
|
||||
- 进度数字跳变
|
||||
- 心率区间颜色过渡
|
||||
- 计时器关键时刻闪烁
|
||||
- 按钮状态点亮 / 失活过渡
|
||||
- 玩法专属状态块的显隐和强调
|
||||
|
||||
### 这一层的特点
|
||||
|
||||
- 更适合 CSS / WXSS animation
|
||||
- 应避免过重
|
||||
- 高优先级字段可以做轻动画,避免全屏大动作
|
||||
|
||||
---
|
||||
|
||||
## 2.3 反馈动画
|
||||
|
||||
这一层最适合和声音、震动一起看,属于事件消费型动画。
|
||||
|
||||
适合放在:
|
||||
|
||||
- `FeedbackDirector`
|
||||
- `UIEffectDirector`
|
||||
|
||||
典型内容:
|
||||
|
||||
- 打点成功 toast
|
||||
- 警告 shake
|
||||
- 成功 burst
|
||||
- stage flash
|
||||
- 局部 pulse
|
||||
- 失败 / 结束反馈
|
||||
|
||||
### 当前已有雏形
|
||||
|
||||
目前系统已经有一些反馈类动效基础:
|
||||
|
||||
- `punchFeedbackFxClass`
|
||||
- `mapPulse`
|
||||
- `stageFx`
|
||||
|
||||
这条线后续最值得继续系统化。
|
||||
|
||||
---
|
||||
|
||||
## 2.4 页面微交互动画
|
||||
|
||||
这一层优先级最低。
|
||||
|
||||
典型内容:
|
||||
|
||||
- 按钮轻微过渡
|
||||
- 面板弹入弹出
|
||||
- 信息卡展开收起
|
||||
- 调试面板展开收起
|
||||
|
||||
### 原则
|
||||
|
||||
- 可以做,但不要先重投入
|
||||
- 不要让它抢过地图和玩法本身的注意力
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前最值得优先打磨的动画
|
||||
|
||||
如果要开始投入动画,我建议先做这 4 组。
|
||||
|
||||
## 3.1 打点成功动画体系
|
||||
|
||||
这是当前项目最值得优先打磨的一组。
|
||||
|
||||
建议包含:
|
||||
|
||||
- 控制点本体状态变化
|
||||
- 地图局部 pulse
|
||||
- HUD 进度跳变
|
||||
- 成功提示 toast
|
||||
- 声音与震动协同
|
||||
|
||||
### 为什么优先
|
||||
|
||||
- 高频
|
||||
- 用户感知强
|
||||
- 直接决定“打点有没有爽感”
|
||||
|
||||
---
|
||||
|
||||
## 3.2 目标点状态动画体系
|
||||
|
||||
建议把目标点的几种状态做清晰区分:
|
||||
|
||||
- 未完成
|
||||
- 当前目标
|
||||
- 可打点
|
||||
- 已完成
|
||||
- 已跳过
|
||||
|
||||
每个状态至少应在:
|
||||
|
||||
- 颜色
|
||||
- 脉冲
|
||||
- 强弱
|
||||
|
||||
上有明显区别。
|
||||
|
||||
### 为什么优先
|
||||
|
||||
- 这是地图玩法的核心视觉语言
|
||||
- 对理解规则和空间注意力引导都很关键
|
||||
|
||||
---
|
||||
|
||||
## 3.3 锁定 / 自动转图状态动画
|
||||
|
||||
建议补强以下体验:
|
||||
|
||||
- 开启 GPS 锁定时的吸附反馈
|
||||
- 锁定关闭时的提示
|
||||
- 自动转图切换时的更自然缓动
|
||||
- 特殊状态下的方向感提示
|
||||
|
||||
### 为什么优先
|
||||
|
||||
- 当前地图交互已经很强
|
||||
- 这块稍微打磨就很有“专业感”
|
||||
|
||||
---
|
||||
|
||||
## 3.4 危险 / 高压状态动画
|
||||
|
||||
这条非常适合未来玩法扩展,尤其是:
|
||||
|
||||
- 幽灵追逐赛
|
||||
- 心率驱动玩法
|
||||
- 高压任务模式
|
||||
|
||||
建议后续支持:
|
||||
|
||||
- 边缘呼吸
|
||||
- 危险圈脉冲
|
||||
- 压力提示颜色递进
|
||||
- 节奏增强
|
||||
|
||||
---
|
||||
|
||||
## 4. 事件驱动建议
|
||||
|
||||
动画最好不要由页面层直接“看到状态变了就自己猜”,而应由事件或 presentation 状态明确驱动。
|
||||
|
||||
建议优先整理以下动画事件:
|
||||
|
||||
- `session_started`
|
||||
- `session_finished`
|
||||
- `session_cancelled`
|
||||
- `control_completed:start`
|
||||
- `control_completed:control`
|
||||
- `control_completed:finish`
|
||||
- `control_skipped`
|
||||
- `guidance_state_changed`
|
||||
- `gps_lock_changed`
|
||||
- `heart_rate_zone_changed`
|
||||
- `danger_state_changed`
|
||||
|
||||
这些事件后续可以统一映射到:
|
||||
|
||||
- sound
|
||||
- haptics
|
||||
- uiEffects
|
||||
- map animation
|
||||
|
||||
---
|
||||
|
||||
## 5. 配置化建议
|
||||
|
||||
后续动画不应只写死在代码里。
|
||||
建议逐步走向 profile 化。
|
||||
|
||||
例如:
|
||||
|
||||
```json
|
||||
"game": {
|
||||
"feedback": {
|
||||
"uiEffectsProfile": "default-race",
|
||||
"mapAnimationProfile": "default-map"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 后续 profile 可承载的内容
|
||||
|
||||
- 某类事件是否启用动效
|
||||
- 动效持续时间
|
||||
- 动效强度
|
||||
- 颜色风格
|
||||
- 是否允许低端机降级
|
||||
|
||||
---
|
||||
|
||||
## 6. 建议增加统一动画配置
|
||||
|
||||
建议后续统一支持:
|
||||
|
||||
```json
|
||||
"game": {
|
||||
"animation": {
|
||||
"enabled": true,
|
||||
"level": "medium"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
建议值:
|
||||
|
||||
- `enabled`
|
||||
- `level = low / medium / high`
|
||||
|
||||
### 用途
|
||||
|
||||
- 低端机降级
|
||||
- 调试关闭
|
||||
- 正式版保守
|
||||
|
||||
---
|
||||
|
||||
## 7. 技术落地建议
|
||||
|
||||
## 7.1 地图动画
|
||||
|
||||
应继续放在地图引擎和 renderer 内处理。
|
||||
|
||||
不要让页面层承担:
|
||||
|
||||
- 点位 pulse
|
||||
- 区域 reveal
|
||||
- 轨迹闪动
|
||||
- 目标高亮
|
||||
|
||||
这些都更适合:
|
||||
|
||||
- `MapPresentation`
|
||||
- `MapScene`
|
||||
- `WebGL renderer`
|
||||
|
||||
---
|
||||
|
||||
## 7.2 HUD 动画
|
||||
|
||||
适合继续放在页面层。
|
||||
|
||||
建议:
|
||||
|
||||
- 尽量轻量
|
||||
- 尽量做过渡,不做大面积复杂动画
|
||||
- 高优先级字段做细微跃迁即可
|
||||
|
||||
---
|
||||
|
||||
## 7.3 反馈动画
|
||||
|
||||
应继续走:
|
||||
|
||||
- `FeedbackDirector`
|
||||
- `UIEffectDirector`
|
||||
|
||||
这条线后续很适合继续统一:
|
||||
|
||||
- 哪个事件触发什么动画
|
||||
- 持续多久
|
||||
- 是否叠加 sound / haptics
|
||||
|
||||
---
|
||||
|
||||
## 8. 实施顺序建议
|
||||
|
||||
不建议一口气铺太多动画。
|
||||
推荐顺序:
|
||||
|
||||
1. `打点成功动画体系`
|
||||
2. `目标点状态动画体系`
|
||||
3. `HUD 数字与状态过渡`
|
||||
4. `锁定 / 自动转图状态动画`
|
||||
5. `危险 / 高压反馈动画`
|
||||
6. 最后再做页面微交互动画
|
||||
|
||||
---
|
||||
|
||||
## 9. 第一阶段建议任务
|
||||
|
||||
如果下一步准备开始做动画,建议第一阶段先只收下面这些:
|
||||
|
||||
### 任务 1
|
||||
|
||||
整理一份动画事件字典:
|
||||
|
||||
- 哪些事件会触发动画
|
||||
- 动画归属哪一层
|
||||
- 对应目的是什么
|
||||
|
||||
### 任务 2
|
||||
|
||||
把打点成功链系统化:
|
||||
|
||||
- 点位变化
|
||||
- HUD 跳变
|
||||
- pulse
|
||||
- toast
|
||||
|
||||
### 任务 3
|
||||
|
||||
统一目标点状态动画:
|
||||
|
||||
- 当前目标
|
||||
- 可打点
|
||||
- 已完成
|
||||
- 已跳过
|
||||
|
||||
### 任务 4
|
||||
|
||||
补一个动画总开关:
|
||||
|
||||
- `animationsEnabled`
|
||||
- `animationLevel`
|
||||
|
||||
---
|
||||
|
||||
## 10. 当前阶段结论
|
||||
|
||||
当前项目已经具备做动画体系的基础。
|
||||
最正确的方向不是继续零散补动效,而是:
|
||||
|
||||
- 先按层组织动画
|
||||
- 再按事件驱动
|
||||
- 最后再做配置化和降级
|
||||
|
||||
一句话总结:
|
||||
|
||||
**后续动画建设应以“打点成功”和“目标状态”两条高频体验为起点,把动画正式纳入现有架构,而不是继续做零散样式补丁。**
|
||||
|
||||
417
doc/archive/config/后台配置管理方案.md
Normal file
417
doc/archive/config/后台配置管理方案.md
Normal file
@@ -0,0 +1,417 @@
|
||||
# 配置驱动应用的后台管理方案建议
|
||||
|
||||
本文用于整理当前这类“配置驱动型地图游戏应用”的后台管理建议,面向:
|
||||
|
||||
- PostgreSQL 数据库
|
||||
- Go 中间层
|
||||
- 后台管理系统
|
||||
- 客户端静态配置发布
|
||||
|
||||
目标是解决一个核心问题:
|
||||
|
||||
**配置文件会越来越大,如何在后台可维护、可复用、可审核、可发布、可回滚。**
|
||||
|
||||
---
|
||||
|
||||
## 1. 总体原则
|
||||
|
||||
最稳的方案不是“数据库直接存一大份 `game.json` 给客户端读”,而是:
|
||||
|
||||
**数据库管理编辑态,发布时编译成运行态静态配置文件。**
|
||||
|
||||
也就是两套形态:
|
||||
|
||||
### 编辑态
|
||||
- 存在 PostgreSQL
|
||||
- 适合后台表单编辑
|
||||
- 支持版本管理
|
||||
- 支持对象复用
|
||||
- 支持审核、比对、回滚
|
||||
|
||||
### 运行态
|
||||
- 由 Go 中间层装配生成
|
||||
- 输出为静态 JSON
|
||||
- 上传到 OSS/CDN
|
||||
- 客户端只读取发布后的静态配置
|
||||
|
||||
这条路线最适合当前项目。
|
||||
|
||||
---
|
||||
|
||||
## 2. 不建议的做法
|
||||
|
||||
不建议把后台做成:
|
||||
|
||||
- 一张表里存一个超大的 `jsonb`
|
||||
- 后台直接编辑整份 `game.json`
|
||||
- 客户端通过 API 动态拼装所有配置
|
||||
|
||||
这样后面会遇到这些问题:
|
||||
|
||||
- 配置复用困难
|
||||
- diff 难看
|
||||
- 回滚困难
|
||||
- 审核困难
|
||||
- 局部编辑体验差
|
||||
- 客户端运行态不稳定
|
||||
|
||||
---
|
||||
|
||||
## 3. 推荐的核心对象
|
||||
|
||||
建议后台和数据库先固定这 5 个核心对象:
|
||||
|
||||
### `Map`
|
||||
地图底座。
|
||||
|
||||
负责:
|
||||
- 瓦片资源
|
||||
- meta 信息
|
||||
- 磁偏角
|
||||
- 初始视角
|
||||
|
||||
### `Playfield`
|
||||
玩法空间对象定义。
|
||||
|
||||
负责:
|
||||
- KML 来源
|
||||
- 控制点覆盖信息
|
||||
- 区域对象
|
||||
- 危险区
|
||||
- 采集物
|
||||
- 起终点信息
|
||||
|
||||
说明:
|
||||
- `Playfield` 是上位概念
|
||||
- `course` 只是其中一种特化形式
|
||||
|
||||
### `GameMode`
|
||||
玩法模板。
|
||||
|
||||
负责:
|
||||
- 顺序赛
|
||||
- 积分赛
|
||||
- 后续幽灵赛、迷雾赛、金币赛等
|
||||
|
||||
也就是:
|
||||
- `game.mode`
|
||||
- `session`
|
||||
- `punch`
|
||||
- `scoring`
|
||||
- `guidance`
|
||||
- `visibility`
|
||||
- `finish`
|
||||
- `telemetry`
|
||||
- `feedback`
|
||||
|
||||
### `ResourcePack`
|
||||
资源包。
|
||||
|
||||
负责:
|
||||
- 音效 profile
|
||||
- 文创内容
|
||||
- 图标
|
||||
- HUD 主题
|
||||
- 动效 profile
|
||||
|
||||
### `Event`
|
||||
最终活动实例。
|
||||
|
||||
负责引用:
|
||||
- 一个 `Map`
|
||||
- 一个 `Playfield`
|
||||
- 一个 `GameMode`
|
||||
- 一个 `ResourcePack`
|
||||
|
||||
并允许少量活动级覆盖。
|
||||
|
||||
一句话:
|
||||
|
||||
**Event = Map + Playfield + GameMode + ResourcePack + EventOverrides**
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据库建模建议
|
||||
|
||||
建议每个核心对象都分成:
|
||||
|
||||
- 主表
|
||||
- version 表
|
||||
|
||||
### 4.1 主表
|
||||
|
||||
主表存稳定元信息:
|
||||
|
||||
- `id`
|
||||
- `slug`
|
||||
- `name`
|
||||
- `status`
|
||||
- `current_version_id`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
### 4.2 version 表
|
||||
|
||||
version 表存每个版本的具体内容:
|
||||
|
||||
- `id`
|
||||
- `parent_id`
|
||||
- `version_no`
|
||||
- `schema_version`
|
||||
- `content_jsonb`
|
||||
- `created_by`
|
||||
- `created_at`
|
||||
- `change_note`
|
||||
|
||||
### 4.3 推荐表
|
||||
|
||||
建议至少有:
|
||||
|
||||
- `maps`
|
||||
- `map_versions`
|
||||
- `playfields`
|
||||
- `playfield_versions`
|
||||
- `game_modes`
|
||||
- `game_mode_versions`
|
||||
- `resource_packs`
|
||||
- `resource_pack_versions`
|
||||
- `events`
|
||||
- `event_versions`
|
||||
|
||||
---
|
||||
|
||||
## 5. 为什么要做版本表
|
||||
|
||||
版本表的价值非常大:
|
||||
|
||||
- 支持草稿
|
||||
- 支持发布版
|
||||
- 支持 diff
|
||||
- 支持回滚
|
||||
- 支持审计
|
||||
- 支持多人协作
|
||||
|
||||
如果没有版本表,后面后台管理一定会越来越难维护。
|
||||
|
||||
---
|
||||
|
||||
## 6. JSONB 的使用建议
|
||||
|
||||
推荐策略是:
|
||||
|
||||
- 稳定字段结构化
|
||||
- 变化快的配置内容放 `jsonb`
|
||||
|
||||
例如主表中:
|
||||
- `slug`
|
||||
- `name`
|
||||
- `status`
|
||||
|
||||
放结构化列。
|
||||
|
||||
而玩法具体配置、资源清单、覆盖字段,放在 `content_jsonb`。
|
||||
|
||||
这样兼顾:
|
||||
- 查询效率
|
||||
- 结构灵活性
|
||||
- 配置扩展性
|
||||
|
||||
---
|
||||
|
||||
## 7. 后台编辑方式建议
|
||||
|
||||
后台不要直接给运营一个大 JSON 编辑框作为主要方式。
|
||||
|
||||
推荐做法:
|
||||
|
||||
- 地图编辑页
|
||||
- Playfield 编辑页
|
||||
- 玩法规则页
|
||||
- 资源包页
|
||||
- 活动编排页
|
||||
|
||||
按模块表单化编辑。
|
||||
|
||||
最后由 Go 中间层负责装配成最终配置 JSON。
|
||||
|
||||
也就是:
|
||||
|
||||
**后台是“编辑结构化对象”,不是“手工拼最终运行文件”。**
|
||||
|
||||
---
|
||||
|
||||
## 8. 发布机制建议
|
||||
|
||||
发布时建议按下面流程:
|
||||
|
||||
1. 后台选定某个 `Event Version`
|
||||
2. Go 中间层读取它引用的:
|
||||
- `Map Version`
|
||||
- `Playfield Version`
|
||||
- `GameMode Version`
|
||||
- `ResourcePack Version`
|
||||
3. 做装配
|
||||
4. 做校验
|
||||
5. 生成最终运行态 JSON
|
||||
6. 上传 OSS/CDN
|
||||
7. 记录一条 release
|
||||
|
||||
客户端只读:
|
||||
- 已发布的静态配置 URL
|
||||
|
||||
不要让客户端直接查数据库 API 动态拼。
|
||||
|
||||
---
|
||||
|
||||
## 9. 推荐增加 Release 层
|
||||
|
||||
建议增加:
|
||||
|
||||
- `event_releases`
|
||||
|
||||
字段例如:
|
||||
|
||||
- `id`
|
||||
- `event_id`
|
||||
- `event_version_id`
|
||||
- `release_no`
|
||||
- `manifest_url`
|
||||
- `published_by`
|
||||
- `published_at`
|
||||
- `status`
|
||||
|
||||
它的作用:
|
||||
|
||||
- 一键回滚
|
||||
- 客户端锁定某次 release
|
||||
- 管理历史发布记录
|
||||
- 灰度验证
|
||||
|
||||
---
|
||||
|
||||
## 10. Go 中间层建议职责
|
||||
|
||||
Go 中间层不要只做 CRUD。
|
||||
|
||||
建议它至少承担这 4 类职责:
|
||||
|
||||
### 10.1 校验
|
||||
- schema 校验
|
||||
- 引用存在校验
|
||||
- 字段完整性校验
|
||||
- 规则约束校验
|
||||
|
||||
### 10.2 装配
|
||||
把:
|
||||
- `Map`
|
||||
- `Playfield`
|
||||
- `GameMode`
|
||||
- `ResourcePack`
|
||||
- `Event Overrides`
|
||||
|
||||
装配成最终配置结构。
|
||||
|
||||
### 10.3 发布
|
||||
- 生成最终静态 JSON
|
||||
- 上传到 OSS/CDN
|
||||
- 记录 release
|
||||
|
||||
### 10.4 对比与预览
|
||||
- 给后台显示 diff
|
||||
- 给发布前做预览
|
||||
|
||||
一句话:
|
||||
|
||||
**Go 中间层本质上是配置编译器。**
|
||||
|
||||
---
|
||||
|
||||
## 11. 校验建议
|
||||
|
||||
建议尽量做强校验。
|
||||
|
||||
至少包括:
|
||||
|
||||
- schemaVersion 合法
|
||||
- 引用对象存在
|
||||
- KML 路径存在
|
||||
- 地图 meta 存在
|
||||
- 玩法字段完整
|
||||
|
||||
以及玩法特定约束,例如:
|
||||
|
||||
- 顺序赛必须有 start / finish
|
||||
- 积分赛 control set 需要 score 或可派生 score
|
||||
- `punch.radiusMeters > 0`
|
||||
- `skip.radiusMeters > punch.radiusMeters`
|
||||
|
||||
这样能把很多错误挡在发布前。
|
||||
|
||||
---
|
||||
|
||||
## 12. 和当前静态目录的关系
|
||||
|
||||
当前你已经有类似目录:
|
||||
|
||||
- `map/`
|
||||
- `kml/`
|
||||
- `event/`
|
||||
|
||||
这很好,可以继续保留。
|
||||
|
||||
建议把它理解成:
|
||||
|
||||
- 数据库 = 编辑态
|
||||
- 这些目录 = 发布产物态
|
||||
|
||||
也就是后台发布后,Go 中间层继续生成:
|
||||
|
||||
- `event/classic-sequential.json`
|
||||
- `event/score-o.json`
|
||||
- `map/...`
|
||||
- `kml/...`
|
||||
|
||||
客户端保持现有读取方式不变。
|
||||
|
||||
---
|
||||
|
||||
## 13. 推荐的后续实施顺序
|
||||
|
||||
建议按这个顺序落地:
|
||||
|
||||
### 第一步
|
||||
先建 5 个核心对象模型:
|
||||
- `Map`
|
||||
- `Playfield`
|
||||
- `GameMode`
|
||||
- `ResourcePack`
|
||||
- `Event`
|
||||
|
||||
### 第二步
|
||||
为每个对象补版本表。
|
||||
|
||||
### 第三步
|
||||
Go 中间层实现“装配成最终 JSON”。
|
||||
|
||||
### 第四步
|
||||
实现“发布到 OSS/CDN”。
|
||||
|
||||
### 第五步
|
||||
后台逐步从 JSON 编辑过渡到模块化表单编辑。
|
||||
|
||||
---
|
||||
|
||||
## 14. 一句话总结
|
||||
|
||||
这类配置驱动应用最稳的后台方案是:
|
||||
|
||||
**PostgreSQL 管结构化、可版本化的编辑态对象;Go 中间层负责校验、装配和发布;客户端只消费发布后的静态 JSON。**
|
||||
|
||||
这样才能做到:
|
||||
|
||||
- 可复用
|
||||
- 可扩展
|
||||
- 可审核
|
||||
- 可回滚
|
||||
- 可稳定运行
|
||||
|
||||
356
doc/archive/config/积分赛配置模板.md
Normal file
356
doc/archive/config/积分赛配置模板.md
Normal file
@@ -0,0 +1,356 @@
|
||||
# 积分赛配置文档(基础版)
|
||||
|
||||
本文档用于给服务端和后台配置设计提供一份可直接落地的积分赛基础模板。
|
||||
目标是先把积分赛入口结构定稳,后续程序功能再逐步细化。
|
||||
|
||||
---
|
||||
|
||||
## 1. 适用玩法
|
||||
|
||||
适用于最基础的积分赛玩法:
|
||||
|
||||
- 手动开始
|
||||
- 先打开始点
|
||||
- 多个检查点自由收集
|
||||
- 控制点有固定分值
|
||||
- 可选终点
|
||||
- 支持选中目标点后打卡
|
||||
|
||||
---
|
||||
|
||||
## 2. 顶层结构
|
||||
|
||||
推荐主配置结构如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.25",
|
||||
"app": {},
|
||||
"map": {},
|
||||
"playfield": {},
|
||||
"game": {},
|
||||
"resources": {},
|
||||
"debug": {}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 完整示例
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.25",
|
||||
"app": {
|
||||
"id": "lxcb-001",
|
||||
"title": "雪熊领秀城区积分赛",
|
||||
"locale": "zh-CN"
|
||||
},
|
||||
"map": {
|
||||
"tiles": "lxcb-001/tiles/",
|
||||
"mapmeta": "lxcb-001/tiles/meta.json",
|
||||
"declination": 6.91,
|
||||
"initialView": {
|
||||
"zoom": 17
|
||||
}
|
||||
},
|
||||
"playfield": {
|
||||
"kind": "control-set",
|
||||
"source": {
|
||||
"type": "kml",
|
||||
"url": "lxcb-001/course/c01.kml"
|
||||
},
|
||||
"CPRadius": 6,
|
||||
"controlOverrides": {
|
||||
"control-1": {
|
||||
"score": 10
|
||||
},
|
||||
"control-2": {
|
||||
"score": 20
|
||||
},
|
||||
"control-3": {
|
||||
"score": 30
|
||||
},
|
||||
"control-4": {
|
||||
"score": 40
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"title": "校园积分赛控制点集",
|
||||
"code": "score-o-c01"
|
||||
}
|
||||
},
|
||||
"game": {
|
||||
"mode": "score-o",
|
||||
"rulesVersion": "1",
|
||||
"session": {
|
||||
"startManually": true,
|
||||
"requiresStartPunch": true,
|
||||
"requiresFinishPunch": false,
|
||||
"autoFinishOnLastControl": false,
|
||||
"maxDurationSec": 5400
|
||||
},
|
||||
"punch": {
|
||||
"policy": "enter-confirm",
|
||||
"radiusMeters": 10,
|
||||
"requiresFocusSelection": true
|
||||
},
|
||||
"scoring": {
|
||||
"type": "score",
|
||||
"defaultControlScore": 10
|
||||
},
|
||||
"guidance": {
|
||||
"showLegs": false,
|
||||
"legAnimation": false,
|
||||
"allowFocusSelection": true
|
||||
},
|
||||
"visibility": {
|
||||
"revealFullPlayfieldAfterStartPunch": true
|
||||
},
|
||||
"finish": {
|
||||
"finishControlAlwaysSelectable": true
|
||||
},
|
||||
"telemetry": {
|
||||
"heartRate": {
|
||||
"age": 30,
|
||||
"restingHeartRateBpm": 62,
|
||||
"userWeightKg": 65
|
||||
}
|
||||
},
|
||||
"feedback": {
|
||||
"audioProfile": "default",
|
||||
"hapticsProfile": "default",
|
||||
"uiEffectsProfile": "default"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"audioProfile": "default",
|
||||
"contentProfile": "default",
|
||||
"themeProfile": "default-race"
|
||||
},
|
||||
"debug": {
|
||||
"allowModeSwitch": false,
|
||||
"allowMockInput": false,
|
||||
"allowSimulator": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 字段说明
|
||||
|
||||
### `app`
|
||||
|
||||
- `id`
|
||||
活动或配置实例 id
|
||||
- `title`
|
||||
活动标题
|
||||
- `locale`
|
||||
语言环境
|
||||
|
||||
### `map`
|
||||
|
||||
- `tiles`
|
||||
瓦片根路径
|
||||
- `mapmeta`
|
||||
地图 meta 地址
|
||||
- `declination`
|
||||
磁偏角
|
||||
- `initialView.zoom`
|
||||
初始缩放级别
|
||||
|
||||
### `playfield`
|
||||
|
||||
- `kind`
|
||||
当前推荐 `control-set`
|
||||
- `source.type`
|
||||
当前推荐 `kml`
|
||||
- `source.url`
|
||||
KML 地址
|
||||
- `CPRadius`
|
||||
检查点绘制半径
|
||||
- `controlOverrides`
|
||||
每个控制点的积分和后续扩展元数据
|
||||
|
||||
### `playfield.controlOverrides`
|
||||
|
||||
当前阶段最推荐至少放:
|
||||
|
||||
- `score`
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
"control-1": {
|
||||
"score": 10
|
||||
}
|
||||
```
|
||||
|
||||
这样可以保证:
|
||||
|
||||
- KML 只提供点位与空间结构
|
||||
- 分值由配置控制
|
||||
|
||||
### `game.session`
|
||||
|
||||
- `startManually`
|
||||
是否手动开始
|
||||
- `requiresStartPunch`
|
||||
是否必须先打开始点
|
||||
- `requiresFinishPunch`
|
||||
是否必须打终点
|
||||
- `autoFinishOnLastControl`
|
||||
积分赛通常为 `false`
|
||||
- `maxDurationSec`
|
||||
最大时长
|
||||
|
||||
### `game.punch`
|
||||
|
||||
- `policy`
|
||||
当前推荐 `enter-confirm`
|
||||
- `radiusMeters`
|
||||
打点半径
|
||||
- `requiresFocusSelection`
|
||||
是否必须先选中目标点后才能打卡
|
||||
|
||||
### `game.scoring`
|
||||
|
||||
- `type`
|
||||
当前推荐 `score`
|
||||
- `defaultControlScore`
|
||||
如果某个点没单独配置分数时的默认值
|
||||
|
||||
### `game.guidance`
|
||||
|
||||
- `showLegs`
|
||||
积分赛基础版建议 `false`
|
||||
- `legAnimation`
|
||||
积分赛基础版建议 `false`
|
||||
- `allowFocusSelection`
|
||||
建议 `true`
|
||||
|
||||
### `game.visibility`
|
||||
|
||||
- `revealFullPlayfieldAfterStartPunch`
|
||||
开始点打卡后是否显示完整控制点集合
|
||||
|
||||
### `game.finish`
|
||||
|
||||
- `finishControlAlwaysSelectable`
|
||||
积分赛建议支持随时选终点结束时,设为 `true`
|
||||
|
||||
### `game.telemetry`
|
||||
|
||||
通用体能参数。
|
||||
|
||||
### `game.feedback`
|
||||
|
||||
反馈 profile 绑定。
|
||||
|
||||
### `resources`
|
||||
|
||||
资源 profile 绑定。
|
||||
|
||||
### `debug`
|
||||
|
||||
调试相关开关。
|
||||
|
||||
---
|
||||
|
||||
## 5. 当前阶段推荐必填字段
|
||||
|
||||
积分赛当前阶段建议至少保证以下字段存在:
|
||||
|
||||
- `map.tiles`
|
||||
- `map.mapmeta`
|
||||
- `map.declination`
|
||||
- `playfield.kind`
|
||||
- `playfield.source.type`
|
||||
- `playfield.source.url`
|
||||
- `playfield.CPRadius`
|
||||
- `game.mode`
|
||||
- `game.punch.policy`
|
||||
- `game.punch.radiusMeters`
|
||||
- `game.punch.requiresFocusSelection`
|
||||
- `game.scoring.type`
|
||||
|
||||
---
|
||||
|
||||
## 6. 当前阶段建议默认值
|
||||
|
||||
如果服务端还没有全部细项,建议先采用以下默认值:
|
||||
|
||||
```json
|
||||
{
|
||||
"game": {
|
||||
"session": {
|
||||
"startManually": true,
|
||||
"requiresStartPunch": true,
|
||||
"requiresFinishPunch": false,
|
||||
"autoFinishOnLastControl": false
|
||||
},
|
||||
"punch": {
|
||||
"policy": "enter-confirm",
|
||||
"radiusMeters": 10,
|
||||
"requiresFocusSelection": true
|
||||
},
|
||||
"scoring": {
|
||||
"type": "score",
|
||||
"defaultControlScore": 10
|
||||
},
|
||||
"guidance": {
|
||||
"showLegs": false,
|
||||
"legAnimation": false,
|
||||
"allowFocusSelection": true
|
||||
},
|
||||
"finish": {
|
||||
"finishControlAlwaysSelectable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 积分赛当前阶段推荐的设计边界
|
||||
|
||||
当前阶段建议坚持以下边界:
|
||||
|
||||
- KML 提供点位和几何底稿
|
||||
- 配置提供分值和玩法解释
|
||||
- 不把积分逻辑写进 KML
|
||||
- 不把自由收集逻辑写成固定路线逻辑
|
||||
|
||||
也就是说:
|
||||
|
||||
- `playfield.kind = control-set`
|
||||
- `controlOverrides.score` 负责分值
|
||||
- `game.guidance.allowFocusSelection = true` 负责选中目标逻辑
|
||||
|
||||
---
|
||||
|
||||
## 8. 适合当前客户端的迁移原则
|
||||
|
||||
当前客户端迁移时,建议服务端先完成:
|
||||
|
||||
1. 将积分赛点位分值迁入 `playfield.controlOverrides`
|
||||
2. 将玩法规则迁入 `game.session / game.punch / game.scoring / game.guidance / game.finish`
|
||||
3. 不急着一次性接入动态积分和复杂规则
|
||||
|
||||
先把静态积分赛入口结构定稳,再逐步扩展动态积分和高级玩法能力。
|
||||
|
||||
---
|
||||
|
||||
## 9. 一句话结论
|
||||
|
||||
积分赛配置当前阶段建议:
|
||||
|
||||
- 用 `playfield.kind = control-set`
|
||||
- 用 KML 承载控制点空间底稿
|
||||
- 用 `playfield.controlOverrides` 承载点位分值
|
||||
- 用 `game.scoring / game.punch / game.guidance / game.finish` 承载玩法规则
|
||||
- 先把静态积分赛入口结构定稳,后续再扩动态积分与更复杂玩法
|
||||
|
||||
588
doc/archive/config/配置设计方案.md
Normal file
588
doc/archive/config/配置设计方案.md
Normal file
@@ -0,0 +1,588 @@
|
||||
# 游戏配置文件设计方案(阶段讨论稿)
|
||||
|
||||
本文档用于整理当前阶段推荐的配置文件设计方案,供后端、客户端和后台管理设计参考。
|
||||
目标是让配置真正成为游戏的驱动入口,同时兼顾后续多玩法、多资源、多活动复用。
|
||||
|
||||
---
|
||||
|
||||
## 1. 设计目标
|
||||
|
||||
配置文件系统需要解决以下问题:
|
||||
|
||||
- 驱动地图、玩法、资源、调试开关
|
||||
- 支持顺序赛、积分赛以及后续更多玩法
|
||||
- 支持将来后台管理系统的内容编排
|
||||
- 保证地图空间信息与玩法语义分层
|
||||
- 保证当前阶段可平滑迁移,不推翻已有实现
|
||||
|
||||
当前推荐原则:
|
||||
|
||||
- 配置只描述,不执行逻辑
|
||||
- 地图、空间对象、玩法规则、资源包分层
|
||||
- KML 负责空间底稿,不负责复杂玩法语义
|
||||
- 主配置先保持单文件,后续再升级为 manifest 组合
|
||||
|
||||
---
|
||||
|
||||
## 2. 顶层配置结构
|
||||
|
||||
当前推荐主入口配置结构如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.25",
|
||||
"app": {},
|
||||
"map": {},
|
||||
"playfield": {},
|
||||
"game": {},
|
||||
"resources": {},
|
||||
"debug": {}
|
||||
}
|
||||
```
|
||||
|
||||
各层职责如下:
|
||||
|
||||
- `app`
|
||||
活动级或应用级基础信息
|
||||
- `map`
|
||||
地图底图和空间底座
|
||||
- `playfield`
|
||||
当前玩法使用的空间对象定义
|
||||
- `game`
|
||||
当前玩法规则配置
|
||||
- `resources`
|
||||
资源包与 profile
|
||||
- `debug`
|
||||
调试与开发开关
|
||||
|
||||
---
|
||||
|
||||
## 3. 为什么不再以 course 作为总抽象
|
||||
|
||||
在定向语义里,`course` 是准确术语,表示路线。
|
||||
但从系统长期扩展看,`course` 并不是所有玩法的上位概念。
|
||||
|
||||
例如:
|
||||
|
||||
- 顺序赛有明显的 `course`
|
||||
- 积分赛更像一组控制点与分数
|
||||
- 金币赛更像可收集点集合
|
||||
- 幽灵赛可能包含危险区、隐身点、追逐者
|
||||
- 迷雾赛可能包含 reveal 点、扫描点、区域
|
||||
|
||||
因此推荐:
|
||||
|
||||
- 将上位内容模型提升为 `playfield`
|
||||
- `course` 只作为 `playfield.kind` 的一种
|
||||
|
||||
例如:
|
||||
|
||||
```json
|
||||
{
|
||||
"playfield": {
|
||||
"kind": "course"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
或:
|
||||
|
||||
```json
|
||||
{
|
||||
"playfield": {
|
||||
"kind": "control-set"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. KML 与配置的边界
|
||||
|
||||
当前推荐边界非常明确:
|
||||
|
||||
### 4.1 KML 负责空间底稿
|
||||
|
||||
KML 适合描述:
|
||||
|
||||
- 点坐标
|
||||
- 起点 / 检查点 / 终点
|
||||
- 顺序号
|
||||
- 点位名称
|
||||
- 腿线几何
|
||||
|
||||
### 4.2 配置负责玩法解释
|
||||
|
||||
配置负责描述:
|
||||
|
||||
- 点位分值
|
||||
- 打点规则
|
||||
- 显隐规则
|
||||
- 动态积分
|
||||
- 道具能力
|
||||
- 迷雾规则
|
||||
- 占领规则
|
||||
- 特殊玩法语义
|
||||
|
||||
一句话总结:
|
||||
|
||||
**KML 描述空间事实,配置描述玩法解释。**
|
||||
|
||||
---
|
||||
|
||||
## 5. 推荐的字段结构
|
||||
|
||||
### 5.1 `app`
|
||||
|
||||
用于活动级基础信息。
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"app": {
|
||||
"id": "lxcb-001",
|
||||
"title": "雪熊领秀城区定向赛",
|
||||
"locale": "zh-CN"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 `map`
|
||||
|
||||
用于地图底图与空间底座。
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"map": {
|
||||
"tiles": "lxcb-001/tiles/",
|
||||
"mapmeta": "lxcb-001/tiles/meta.json",
|
||||
"declination": 6.91,
|
||||
"initialView": {
|
||||
"zoom": 17
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 `playfield`
|
||||
|
||||
用于描述当前玩法使用的空间对象及其来源。
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"playfield": {
|
||||
"kind": "course",
|
||||
"source": {
|
||||
"type": "kml",
|
||||
"url": "lxcb-001/course/c01.kml"
|
||||
},
|
||||
"CPRadius": 6,
|
||||
"controlOverrides": {},
|
||||
"metadata": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
建议后续逐步支持的对象包括:
|
||||
|
||||
- `controls`
|
||||
- `collectibles`
|
||||
- `zones`
|
||||
- `hazards`
|
||||
- `links`
|
||||
- `spawnPoints`
|
||||
|
||||
### 5.4 `game`
|
||||
|
||||
用于描述玩法规则。
|
||||
|
||||
推荐统一结构如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"game": {
|
||||
"mode": "",
|
||||
"rulesVersion": "1",
|
||||
"session": {},
|
||||
"punch": {},
|
||||
"scoring": {},
|
||||
"guidance": {},
|
||||
"visibility": {},
|
||||
"finish": {},
|
||||
"telemetry": {},
|
||||
"feedback": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `session`
|
||||
|
||||
控制一局游戏的流程参数:
|
||||
|
||||
- 是否手动开始
|
||||
- 是否必须打开始点
|
||||
- 是否必须打结束点
|
||||
- 是否允许自动结束
|
||||
- 最大时长
|
||||
|
||||
#### `punch`
|
||||
|
||||
控制打点规则:
|
||||
|
||||
- 打点策略
|
||||
- 打点半径
|
||||
- 是否必须选中后打卡
|
||||
|
||||
#### `scoring`
|
||||
|
||||
控制积分与结算:
|
||||
|
||||
- 完成型
|
||||
- 固定分
|
||||
- 动态分
|
||||
|
||||
#### `guidance`
|
||||
|
||||
控制引导方式:
|
||||
|
||||
- 是否显示腿线
|
||||
- 是否显示腿线动画
|
||||
- 是否允许 focus 选择
|
||||
|
||||
#### `visibility`
|
||||
|
||||
控制显隐逻辑:
|
||||
|
||||
- 是否开始后显示全图
|
||||
- 是否采用迷雾
|
||||
|
||||
#### `finish`
|
||||
|
||||
控制结束规则:
|
||||
|
||||
- 是否必须打终点
|
||||
- 是否允许随时结束
|
||||
|
||||
#### `telemetry`
|
||||
|
||||
控制通用运动信息参数:
|
||||
|
||||
- 年龄
|
||||
- 静息心率
|
||||
- 体重
|
||||
|
||||
#### `feedback`
|
||||
|
||||
控制反馈 profile:
|
||||
|
||||
- 音频
|
||||
- 震动
|
||||
- UI 动效
|
||||
|
||||
### 5.5 `resources`
|
||||
|
||||
用于描述资源 profile。
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"resources": {
|
||||
"audioProfile": "default",
|
||||
"contentProfile": "default",
|
||||
"themeProfile": "default-race"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当前阶段建议先保持轻量,后续再逐步拆成资源包 manifest。
|
||||
|
||||
### 5.6 `debug`
|
||||
|
||||
用于开发和调试相关开关。
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"debug": {
|
||||
"allowModeSwitch": false,
|
||||
"allowMockInput": false,
|
||||
"allowSimulator": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 顺序赛示例配置
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.25",
|
||||
"app": {
|
||||
"id": "lxcb-001",
|
||||
"title": "雪熊领秀城区顺序赛"
|
||||
},
|
||||
"map": {
|
||||
"tiles": "lxcb-001/tiles/",
|
||||
"mapmeta": "lxcb-001/tiles/meta.json",
|
||||
"declination": 6.91
|
||||
},
|
||||
"playfield": {
|
||||
"kind": "course",
|
||||
"source": {
|
||||
"type": "kml",
|
||||
"url": "lxcb-001/course/c01.kml"
|
||||
},
|
||||
"CPRadius": 6
|
||||
},
|
||||
"game": {
|
||||
"mode": "classic-sequential",
|
||||
"rulesVersion": "1",
|
||||
"session": {
|
||||
"requiresStartPunch": true,
|
||||
"requiresFinishPunch": true,
|
||||
"autoFinishOnLastControl": false,
|
||||
"startManually": true
|
||||
},
|
||||
"punch": {
|
||||
"policy": "enter-confirm",
|
||||
"radiusMeters": 10
|
||||
},
|
||||
"guidance": {
|
||||
"showLegs": true,
|
||||
"legAnimation": true,
|
||||
"allowFocusSelection": false
|
||||
},
|
||||
"visibility": {
|
||||
"revealFullPlayfieldAfterStartPunch": true
|
||||
},
|
||||
"telemetry": {
|
||||
"heartRate": {
|
||||
"age": 30,
|
||||
"restingHeartRateBpm": 62,
|
||||
"userWeightKg": 65
|
||||
}
|
||||
},
|
||||
"feedback": {
|
||||
"audioProfile": "default",
|
||||
"hapticsProfile": "default",
|
||||
"uiEffectsProfile": "default"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"audioProfile": "default",
|
||||
"contentProfile": "default"
|
||||
},
|
||||
"debug": {
|
||||
"allowModeSwitch": false,
|
||||
"allowMockInput": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 积分赛示例配置
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.25",
|
||||
"app": {
|
||||
"id": "lxcb-001",
|
||||
"title": "雪熊领秀城区积分赛"
|
||||
},
|
||||
"map": {
|
||||
"tiles": "lxcb-001/tiles/",
|
||||
"mapmeta": "lxcb-001/tiles/meta.json",
|
||||
"declination": 6.91
|
||||
},
|
||||
"playfield": {
|
||||
"kind": "control-set",
|
||||
"source": {
|
||||
"type": "kml",
|
||||
"url": "lxcb-001/course/c01.kml"
|
||||
},
|
||||
"CPRadius": 6,
|
||||
"controlOverrides": {
|
||||
"control-1": { "score": 10 },
|
||||
"control-2": { "score": 20 },
|
||||
"control-3": { "score": 30 }
|
||||
}
|
||||
},
|
||||
"game": {
|
||||
"mode": "score-o",
|
||||
"rulesVersion": "1",
|
||||
"session": {
|
||||
"requiresStartPunch": true,
|
||||
"requiresFinishPunch": false,
|
||||
"startManually": true
|
||||
},
|
||||
"punch": {
|
||||
"policy": "enter-confirm",
|
||||
"radiusMeters": 10,
|
||||
"requiresFocusSelection": true
|
||||
},
|
||||
"guidance": {
|
||||
"showLegs": false,
|
||||
"legAnimation": false,
|
||||
"allowFocusSelection": true
|
||||
},
|
||||
"scoring": {
|
||||
"type": "score"
|
||||
},
|
||||
"finish": {
|
||||
"finishControlAlwaysSelectable": true
|
||||
},
|
||||
"telemetry": {
|
||||
"heartRate": {
|
||||
"age": 30,
|
||||
"restingHeartRateBpm": 62,
|
||||
"userWeightKg": 65
|
||||
}
|
||||
},
|
||||
"feedback": {
|
||||
"audioProfile": "default",
|
||||
"hapticsProfile": "default",
|
||||
"uiEffectsProfile": "default"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"audioProfile": "default",
|
||||
"contentProfile": "default"
|
||||
},
|
||||
"debug": {
|
||||
"allowModeSwitch": false,
|
||||
"allowMockInput": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 当前老字段到新结构的迁移建议
|
||||
|
||||
### 地图层
|
||||
|
||||
- `map` -> `map.tiles`
|
||||
- `mapmeta` -> `map.mapmeta`
|
||||
- `declination` -> `map.declination`
|
||||
|
||||
### 路线层
|
||||
|
||||
- `course` -> `playfield.source.url`
|
||||
- `CPRadius` -> `playfield.CPRadius`
|
||||
|
||||
### 玩法层
|
||||
|
||||
- `game.mode` -> `game.mode`
|
||||
- `game.punchPolicy` -> `game.punch.policy`
|
||||
- `PunchRadius` -> `game.punch.radiusMeters`
|
||||
- `game.autoFinishOnLastControl` -> `game.session.autoFinishOnLastControl`
|
||||
|
||||
### telemetry 层
|
||||
|
||||
- `game.telemetry.age` -> `game.telemetry.heartRate.age`
|
||||
- `game.telemetry.restingHeartRateBpm` -> `game.telemetry.heartRate.restingHeartRateBpm`
|
||||
- `game.telemetry.userWeightKg` -> `game.telemetry.heartRate.userWeightKg`
|
||||
|
||||
### feedback 层
|
||||
|
||||
- `game.audio` -> `game.feedback.audio` 或 `resources.audioProfiles`
|
||||
- `game.haptics` -> `game.feedback.haptics` 或 `resources.hapticsProfiles`
|
||||
- `game.uiEffects` -> `game.feedback.uiEffects` 或 `resources.uiEffectsProfiles`
|
||||
|
||||
当前建议迁移策略:
|
||||
|
||||
- 第一阶段:代码同时兼容老字段和新结构
|
||||
- 第二阶段:线上配置逐步切换
|
||||
- 第三阶段:再清理旧字段兼容逻辑
|
||||
|
||||
---
|
||||
|
||||
## 9. 未来推荐的 manifest 方向
|
||||
|
||||
当前阶段主配置建议先保持单文件。
|
||||
但未来配置规模变大时,推荐升级成多 manifest 组合:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.25",
|
||||
"map": {
|
||||
"manifest": "maps/lxcb-001/map.json"
|
||||
},
|
||||
"playfield": {
|
||||
"manifest": "playfields/lxcb-001/c01.json"
|
||||
},
|
||||
"game": {
|
||||
"manifest": "modes/score-o/default.json"
|
||||
},
|
||||
"resources": {
|
||||
"manifest": "packs/spring-2026/resources.json"
|
||||
},
|
||||
"debug": {}
|
||||
}
|
||||
```
|
||||
|
||||
这样可以支持:
|
||||
|
||||
- 一张地图挂多种玩法
|
||||
- 一条 playfield 挂多种规则
|
||||
- 一种玩法切换不同资源包
|
||||
- 后台管理做拼装式发布
|
||||
|
||||
---
|
||||
|
||||
## 10. 服务端和后台管理的推荐核心对象
|
||||
|
||||
后续从服务端和后台管理的复用角度,建议围绕以下核心对象建模:
|
||||
|
||||
- `Map`
|
||||
- `Playfield`
|
||||
- `GameMode`
|
||||
- `ResourcePack`
|
||||
- `Event`
|
||||
|
||||
其中:
|
||||
|
||||
- `Map`
|
||||
地图底图与空间底座
|
||||
- `Playfield`
|
||||
当前玩法场景中的空间对象定义
|
||||
- `GameMode`
|
||||
玩法规则模板
|
||||
- `ResourcePack`
|
||||
资源包与 profile
|
||||
- `Event`
|
||||
一次实际发布的活动实例
|
||||
|
||||
推荐关系可以理解为:
|
||||
|
||||
`Event = Map + Playfield + GameMode + ResourcePack + 发布参数`
|
||||
|
||||
---
|
||||
|
||||
## 11. 当前阶段推荐结论
|
||||
|
||||
当前阶段最推荐的方案是:
|
||||
|
||||
- 先保留单个 `game.json`
|
||||
- 结构升级为 `app / map / playfield / game / resources / debug`
|
||||
- 保留 KML 作为空间底稿来源
|
||||
- 不再让 `course` 成为总抽象,而是提升为更通用的 `playfield`
|
||||
- 让代码先双兼容,再逐步迁移线上配置
|
||||
|
||||
一句话总结:
|
||||
|
||||
**KML 描述空间事实,配置描述玩法解释;主配置按 `map / playfield / game / resources / debug` 分层,后续再升级成 manifest 组合。**
|
||||
|
||||
314
doc/archive/config/顺序赛配置模板.md
Normal file
314
doc/archive/config/顺序赛配置模板.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# 顺序赛配置文档(基础版)
|
||||
|
||||
本文档用于给服务端和后台配置设计提供一份可直接落地的顺序赛基础模板。
|
||||
目标是先把入口配置结构定稳,后续程序功能再逐步细化。
|
||||
|
||||
---
|
||||
|
||||
## 1. 适用玩法
|
||||
|
||||
适用于最基础的顺序打点玩法:
|
||||
|
||||
- 手动开始
|
||||
- 先打开始点
|
||||
- 按顺序打检查点
|
||||
- 最后打终点
|
||||
- 支持正常打点半径
|
||||
- 预留后续扩展规则空间
|
||||
|
||||
---
|
||||
|
||||
## 2. 顶层结构
|
||||
|
||||
推荐主配置结构如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.25",
|
||||
"app": {},
|
||||
"map": {},
|
||||
"playfield": {},
|
||||
"game": {},
|
||||
"resources": {},
|
||||
"debug": {}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 完整示例
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.25",
|
||||
"app": {
|
||||
"id": "lxcb-001",
|
||||
"title": "雪熊领秀城区顺序赛",
|
||||
"locale": "zh-CN"
|
||||
},
|
||||
"map": {
|
||||
"tiles": "lxcb-001/tiles/",
|
||||
"mapmeta": "lxcb-001/tiles/meta.json",
|
||||
"declination": 6.91,
|
||||
"initialView": {
|
||||
"zoom": 17
|
||||
}
|
||||
},
|
||||
"playfield": {
|
||||
"kind": "course",
|
||||
"source": {
|
||||
"type": "kml",
|
||||
"url": "lxcb-001/course/c01.kml"
|
||||
},
|
||||
"CPRadius": 6,
|
||||
"metadata": {
|
||||
"title": "校园经典路线",
|
||||
"code": "c01"
|
||||
}
|
||||
},
|
||||
"game": {
|
||||
"mode": "classic-sequential",
|
||||
"rulesVersion": "1",
|
||||
"session": {
|
||||
"startManually": true,
|
||||
"requiresStartPunch": true,
|
||||
"requiresFinishPunch": true,
|
||||
"autoFinishOnLastControl": false,
|
||||
"maxDurationSec": 5400
|
||||
},
|
||||
"punch": {
|
||||
"policy": "enter-confirm",
|
||||
"radiusMeters": 10
|
||||
},
|
||||
"sequence": {
|
||||
"skip": {
|
||||
"enabled": false,
|
||||
"radiusMeters": 30,
|
||||
"requiresConfirm": true
|
||||
}
|
||||
},
|
||||
"guidance": {
|
||||
"showLegs": true,
|
||||
"legAnimation": true,
|
||||
"allowFocusSelection": false
|
||||
},
|
||||
"visibility": {
|
||||
"revealFullPlayfieldAfterStartPunch": true
|
||||
},
|
||||
"finish": {
|
||||
"finishControlAlwaysSelectable": false
|
||||
},
|
||||
"telemetry": {
|
||||
"heartRate": {
|
||||
"age": 30,
|
||||
"restingHeartRateBpm": 62,
|
||||
"userWeightKg": 65
|
||||
}
|
||||
},
|
||||
"feedback": {
|
||||
"audioProfile": "default",
|
||||
"hapticsProfile": "default",
|
||||
"uiEffectsProfile": "default"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"audioProfile": "default",
|
||||
"contentProfile": "default",
|
||||
"themeProfile": "default-race"
|
||||
},
|
||||
"debug": {
|
||||
"allowModeSwitch": false,
|
||||
"allowMockInput": false,
|
||||
"allowSimulator": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 字段说明
|
||||
|
||||
### `app`
|
||||
|
||||
- `id`
|
||||
活动或配置实例 id
|
||||
- `title`
|
||||
活动标题
|
||||
- `locale`
|
||||
语言环境
|
||||
|
||||
### `map`
|
||||
|
||||
- `tiles`
|
||||
瓦片根路径
|
||||
- `mapmeta`
|
||||
地图 meta 地址
|
||||
- `declination`
|
||||
磁偏角
|
||||
- `initialView.zoom`
|
||||
初始缩放级别
|
||||
|
||||
### `playfield`
|
||||
|
||||
- `kind`
|
||||
当前为 `course`
|
||||
- `source.type`
|
||||
当前推荐为 `kml`
|
||||
- `source.url`
|
||||
KML 地址
|
||||
- `CPRadius`
|
||||
检查点绘制半径,单位米
|
||||
- `metadata`
|
||||
路线元数据
|
||||
|
||||
### `game.session`
|
||||
|
||||
- `startManually`
|
||||
是否需要先点击开始按钮
|
||||
- `requiresStartPunch`
|
||||
是否必须先打开始点
|
||||
- `requiresFinishPunch`
|
||||
是否必须打终点
|
||||
- `autoFinishOnLastControl`
|
||||
是否打完最后一个检查点自动结束
|
||||
- `maxDurationSec`
|
||||
最大比赛时长
|
||||
|
||||
### `game.punch`
|
||||
|
||||
- `policy`
|
||||
当前推荐 `enter-confirm`
|
||||
- `radiusMeters`
|
||||
正常打点半径
|
||||
|
||||
### `game.sequence`
|
||||
|
||||
- `skip`
|
||||
顺序赛跳点规则
|
||||
- `enabled`
|
||||
是否允许跳点
|
||||
- `radiusMeters`
|
||||
跳点半径,必须大于打点半径
|
||||
- `requiresConfirm`
|
||||
是否必须用户确认后跳点
|
||||
|
||||
当前基础版建议先关闭:
|
||||
|
||||
```json
|
||||
"enabled": false
|
||||
```
|
||||
|
||||
### `game.guidance`
|
||||
|
||||
- `showLegs`
|
||||
是否显示腿线
|
||||
- `legAnimation`
|
||||
是否显示当前腿动画
|
||||
- `allowFocusSelection`
|
||||
顺序赛一般为 `false`
|
||||
|
||||
### `game.visibility`
|
||||
|
||||
- `revealFullPlayfieldAfterStartPunch`
|
||||
开始点打卡后是否显示完整路线
|
||||
|
||||
### `game.finish`
|
||||
|
||||
- `finishControlAlwaysSelectable`
|
||||
顺序赛一般为 `false`
|
||||
|
||||
### `game.telemetry`
|
||||
|
||||
通用体能参数。
|
||||
|
||||
### `game.feedback`
|
||||
|
||||
反馈 profile 绑定。
|
||||
|
||||
### `resources`
|
||||
|
||||
资源 profile 绑定。
|
||||
|
||||
### `debug`
|
||||
|
||||
调试相关开关。
|
||||
|
||||
---
|
||||
|
||||
## 5. 当前阶段推荐必填字段
|
||||
|
||||
顺序赛当前阶段建议至少保证以下字段存在:
|
||||
|
||||
- `map.tiles`
|
||||
- `map.mapmeta`
|
||||
- `map.declination`
|
||||
- `playfield.kind`
|
||||
- `playfield.source.type`
|
||||
- `playfield.source.url`
|
||||
- `playfield.CPRadius`
|
||||
- `game.mode`
|
||||
- `game.punch.policy`
|
||||
- `game.punch.radiusMeters`
|
||||
- `game.session.requiresStartPunch`
|
||||
- `game.session.requiresFinishPunch`
|
||||
|
||||
---
|
||||
|
||||
## 6. 当前阶段建议默认值
|
||||
|
||||
如果服务端还没有全部配置细项,建议先采用以下默认值:
|
||||
|
||||
```json
|
||||
{
|
||||
"game": {
|
||||
"session": {
|
||||
"startManually": true,
|
||||
"requiresStartPunch": true,
|
||||
"requiresFinishPunch": true,
|
||||
"autoFinishOnLastControl": false
|
||||
},
|
||||
"punch": {
|
||||
"policy": "enter-confirm",
|
||||
"radiusMeters": 10
|
||||
},
|
||||
"sequence": {
|
||||
"skip": {
|
||||
"enabled": false,
|
||||
"radiusMeters": 30,
|
||||
"requiresConfirm": true
|
||||
}
|
||||
},
|
||||
"guidance": {
|
||||
"showLegs": true,
|
||||
"legAnimation": true,
|
||||
"allowFocusSelection": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 适合当前客户端的迁移原则
|
||||
|
||||
当前客户端迁移时,建议服务端先完成:
|
||||
|
||||
1. 将老字段逐步迁入 `map / playfield / game / resources / debug`
|
||||
2. 保持基础字段完整
|
||||
3. 不急着一次性把所有高级规则上线
|
||||
|
||||
优先把“入口结构”夯实,再逐步扩玩法参数。
|
||||
|
||||
---
|
||||
|
||||
## 8. 一句话结论
|
||||
|
||||
顺序赛配置当前阶段建议:
|
||||
|
||||
- 用 `playfield.kind = course`
|
||||
- 用 KML 承载空间底稿
|
||||
- 用 `game.session / game.punch / game.sequence / game.guidance` 承载玩法规则
|
||||
- 先把基础入口结构定稳,后续再细化跳点、惩罚、特殊引导等高级规则
|
||||
|
||||
416
doc/archive/config/默认配置模板.md
Normal file
416
doc/archive/config/默认配置模板.md
Normal file
@@ -0,0 +1,416 @@
|
||||
# 默认配置模板文档(当前实现版)
|
||||
|
||||
本文档提供一份 **当前客户端可直接使用的默认配置模板**。
|
||||
目标是:
|
||||
|
||||
- 给服务端/后台一个稳定的起步模板
|
||||
- 保证即使只填最少字段,也能正常跑起来
|
||||
- 随开发持续补充和维护
|
||||
|
||||
说明:
|
||||
|
||||
- 本模板优先保证“可运行”
|
||||
- 高级字段可以逐步补
|
||||
- 文创内容和点击内容也已经纳入模板
|
||||
|
||||
---
|
||||
|
||||
## 1. 顶层默认模板
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.27",
|
||||
"app": {
|
||||
"id": "sample-event-001",
|
||||
"title": "示例活动",
|
||||
"locale": "zh-CN"
|
||||
},
|
||||
"map": {
|
||||
"tiles": "../map/lxcb-001/tiles/",
|
||||
"mapmeta": "../map/lxcb-001/tiles/meta.json",
|
||||
"declination": 6.91,
|
||||
"initialView": {
|
||||
"zoom": 17
|
||||
}
|
||||
},
|
||||
"playfield": {
|
||||
"kind": "course",
|
||||
"source": {
|
||||
"type": "kml",
|
||||
"url": "../kml/lxcb-001/10/c01.kml"
|
||||
},
|
||||
"CPRadius": 6,
|
||||
"controlOverrides": {},
|
||||
"metadata": {
|
||||
"title": "默认路线",
|
||||
"code": "default-001"
|
||||
}
|
||||
},
|
||||
"game": {
|
||||
"mode": "classic-sequential",
|
||||
"rulesVersion": "1",
|
||||
"session": {
|
||||
"startManually": true,
|
||||
"requiresStartPunch": true,
|
||||
"requiresFinishPunch": true,
|
||||
"autoFinishOnLastControl": false,
|
||||
"maxDurationSec": 5400
|
||||
},
|
||||
"punch": {
|
||||
"policy": "enter-confirm",
|
||||
"radiusMeters": 5,
|
||||
"requiresFocusSelection": false
|
||||
},
|
||||
"sequence": {
|
||||
"skip": {
|
||||
"enabled": false,
|
||||
"radiusMeters": 30,
|
||||
"requiresConfirm": true
|
||||
}
|
||||
},
|
||||
"scoring": {
|
||||
"type": "score",
|
||||
"defaultControlScore": 10
|
||||
},
|
||||
"guidance": {
|
||||
"showLegs": true,
|
||||
"legAnimation": true,
|
||||
"allowFocusSelection": false
|
||||
},
|
||||
"visibility": {
|
||||
"revealFullPlayfieldAfterStartPunch": true
|
||||
},
|
||||
"finish": {
|
||||
"finishControlAlwaysSelectable": false
|
||||
},
|
||||
"telemetry": {
|
||||
"heartRate": {
|
||||
"age": 30,
|
||||
"restingHeartRateBpm": 62,
|
||||
"userWeightKg": 65
|
||||
}
|
||||
},
|
||||
"feedback": {
|
||||
"audioProfile": "default",
|
||||
"hapticsProfile": "default",
|
||||
"uiEffectsProfile": "default"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"audioProfile": "default",
|
||||
"contentProfile": "default",
|
||||
"themeProfile": "default-race"
|
||||
},
|
||||
"debug": {
|
||||
"allowModeSwitch": false,
|
||||
"allowMockInput": false,
|
||||
"allowSimulator": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 顺序赛推荐默认模板
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.27",
|
||||
"app": {
|
||||
"id": "sample-classic-001",
|
||||
"title": "顺序赛示例",
|
||||
"locale": "zh-CN"
|
||||
},
|
||||
"map": {
|
||||
"tiles": "../map/lxcb-001/tiles/",
|
||||
"mapmeta": "../map/lxcb-001/tiles/meta.json",
|
||||
"declination": 6.91,
|
||||
"initialView": {
|
||||
"zoom": 17
|
||||
}
|
||||
},
|
||||
"playfield": {
|
||||
"kind": "course",
|
||||
"source": {
|
||||
"type": "kml",
|
||||
"url": "../kml/lxcb-001/10/c01.kml"
|
||||
},
|
||||
"CPRadius": 6,
|
||||
"controlOverrides": {
|
||||
"start-1": {
|
||||
"title": "比赛开始",
|
||||
"body": "从这里出发,先熟悉地图方向。",
|
||||
"autoPopup": true,
|
||||
"once": true,
|
||||
"priority": 1,
|
||||
"clickTitle": "起点说明",
|
||||
"clickBody": "点击起点可再次查看起跑说明。"
|
||||
},
|
||||
"control-1": {
|
||||
"title": "第一检查点",
|
||||
"body": "完成这个点后沿主路继续前进。",
|
||||
"autoPopup": true,
|
||||
"once": false,
|
||||
"priority": 1,
|
||||
"clickTitle": "第一检查点",
|
||||
"clickBody": "点击查看该点位的补充说明。"
|
||||
},
|
||||
"finish-1": {
|
||||
"title": "比赛结束",
|
||||
"body": "恭喜完成本次路线。",
|
||||
"autoPopup": true,
|
||||
"once": true,
|
||||
"priority": 2,
|
||||
"clickTitle": "终点说明",
|
||||
"clickBody": "点击终点可再次查看结束说明。"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"title": "顺序赛路线示例",
|
||||
"code": "classic-001"
|
||||
}
|
||||
},
|
||||
"game": {
|
||||
"mode": "classic-sequential",
|
||||
"rulesVersion": "1",
|
||||
"session": {
|
||||
"startManually": true,
|
||||
"requiresStartPunch": true,
|
||||
"requiresFinishPunch": true,
|
||||
"autoFinishOnLastControl": false,
|
||||
"maxDurationSec": 5400
|
||||
},
|
||||
"punch": {
|
||||
"policy": "enter-confirm",
|
||||
"radiusMeters": 5,
|
||||
"requiresFocusSelection": false
|
||||
},
|
||||
"sequence": {
|
||||
"skip": {
|
||||
"enabled": false,
|
||||
"radiusMeters": 30,
|
||||
"requiresConfirm": true
|
||||
}
|
||||
},
|
||||
"guidance": {
|
||||
"showLegs": true,
|
||||
"legAnimation": true,
|
||||
"allowFocusSelection": false
|
||||
},
|
||||
"visibility": {
|
||||
"revealFullPlayfieldAfterStartPunch": true
|
||||
},
|
||||
"finish": {
|
||||
"finishControlAlwaysSelectable": false
|
||||
},
|
||||
"telemetry": {
|
||||
"heartRate": {
|
||||
"age": 30,
|
||||
"restingHeartRateBpm": 62,
|
||||
"userWeightKg": 65
|
||||
}
|
||||
},
|
||||
"feedback": {
|
||||
"audioProfile": "default",
|
||||
"hapticsProfile": "default",
|
||||
"uiEffectsProfile": "default"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"audioProfile": "default",
|
||||
"contentProfile": "default",
|
||||
"themeProfile": "default-race"
|
||||
},
|
||||
"debug": {
|
||||
"allowModeSwitch": false,
|
||||
"allowMockInput": false,
|
||||
"allowSimulator": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 积分赛推荐默认模板
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1",
|
||||
"version": "2026.03.27",
|
||||
"app": {
|
||||
"id": "sample-score-o-001",
|
||||
"title": "积分赛示例",
|
||||
"locale": "zh-CN"
|
||||
},
|
||||
"map": {
|
||||
"tiles": "../map/lxcb-001/tiles/",
|
||||
"mapmeta": "../map/lxcb-001/tiles/meta.json",
|
||||
"declination": 6.91,
|
||||
"initialView": {
|
||||
"zoom": 17
|
||||
}
|
||||
},
|
||||
"playfield": {
|
||||
"kind": "control-set",
|
||||
"source": {
|
||||
"type": "kml",
|
||||
"url": "../kml/lxcb-001/10/c01.kml"
|
||||
},
|
||||
"CPRadius": 6,
|
||||
"controlOverrides": {
|
||||
"start-1": {
|
||||
"title": "比赛开始",
|
||||
"body": "从这里触发,先熟悉地图方向。",
|
||||
"autoPopup": true,
|
||||
"once": true,
|
||||
"priority": 1,
|
||||
"clickTitle": "积分赛起点",
|
||||
"clickBody": "点击起点可查看自由打点规则。"
|
||||
},
|
||||
"control-1": {
|
||||
"score": 10,
|
||||
"clickTitle": "1号点",
|
||||
"clickBody": "这是一个基础积分点。"
|
||||
},
|
||||
"control-2": {
|
||||
"score": 20,
|
||||
"autoPopup": false,
|
||||
"once": true,
|
||||
"priority": 1,
|
||||
"clickTitle": "2号点",
|
||||
"clickBody": "这个点配置成点击查看。"
|
||||
},
|
||||
"finish-1": {
|
||||
"title": "比赛结束",
|
||||
"body": "恭喜完成本次路线。",
|
||||
"autoPopup": true,
|
||||
"once": true,
|
||||
"priority": 2,
|
||||
"clickTitle": "终点说明",
|
||||
"clickBody": "点击终点可再次查看结束说明。"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"title": "积分赛控制点示例",
|
||||
"code": "score-o-001"
|
||||
}
|
||||
},
|
||||
"game": {
|
||||
"mode": "score-o",
|
||||
"rulesVersion": "1",
|
||||
"session": {
|
||||
"startManually": true,
|
||||
"requiresStartPunch": true,
|
||||
"requiresFinishPunch": false,
|
||||
"autoFinishOnLastControl": false,
|
||||
"maxDurationSec": 5400
|
||||
},
|
||||
"punch": {
|
||||
"policy": "enter-confirm",
|
||||
"radiusMeters": 5,
|
||||
"requiresFocusSelection": false
|
||||
},
|
||||
"scoring": {
|
||||
"type": "score",
|
||||
"defaultControlScore": 10
|
||||
},
|
||||
"guidance": {
|
||||
"showLegs": false,
|
||||
"legAnimation": false,
|
||||
"allowFocusSelection": true
|
||||
},
|
||||
"visibility": {
|
||||
"revealFullPlayfieldAfterStartPunch": true
|
||||
},
|
||||
"finish": {
|
||||
"finishControlAlwaysSelectable": true
|
||||
},
|
||||
"telemetry": {
|
||||
"heartRate": {
|
||||
"age": 30,
|
||||
"restingHeartRateBpm": 62,
|
||||
"userWeightKg": 65
|
||||
}
|
||||
},
|
||||
"feedback": {
|
||||
"audioProfile": "default",
|
||||
"hapticsProfile": "default",
|
||||
"uiEffectsProfile": "default"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"audioProfile": "default",
|
||||
"contentProfile": "default",
|
||||
"themeProfile": "default-race"
|
||||
},
|
||||
"debug": {
|
||||
"allowModeSwitch": false,
|
||||
"allowMockInput": false,
|
||||
"allowSimulator": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 默认逻辑说明
|
||||
|
||||
### 4.1 内容展示默认逻辑
|
||||
|
||||
- `title/body`
|
||||
- 未配置时使用系统默认文案
|
||||
- `clickTitle/clickBody`
|
||||
- 未配置时回退到 `title/body`
|
||||
- `autoPopup`
|
||||
- 默认允许自动弹出
|
||||
- `once`
|
||||
- 默认 `false`
|
||||
- `priority`
|
||||
- 普通点默认 `1`
|
||||
- 终点默认 `2`
|
||||
- 自动打点时:
|
||||
- 自动打点完成后不自动弹内容
|
||||
- 点击内容仍可用
|
||||
|
||||
### 4.2 玩法默认逻辑
|
||||
|
||||
- 顺序赛默认:
|
||||
- 必须起点
|
||||
- 必须终点
|
||||
- 不自动结束
|
||||
- 跳点默认关闭
|
||||
- 积分赛默认:
|
||||
- 必须起点
|
||||
- 终点可选
|
||||
- 不自动结束
|
||||
- 默认分值 `10`
|
||||
|
||||
### 4.3 资源默认逻辑
|
||||
|
||||
- `audioProfile = default`
|
||||
- `contentProfile = default`
|
||||
- `themeProfile = default-race`
|
||||
|
||||
---
|
||||
|
||||
## 5. 建议维护方式
|
||||
|
||||
后续每次配置能力扩展时,建议同步维护:
|
||||
|
||||
1. [D:\dev\cmr-mini\config-option-dictionary.md](D:/dev/cmr-mini/doc/doc/config-option-dictionary.md)
|
||||
2. [D:\dev\cmr-mini\config-default-template.md](D:/dev/cmr-mini/doc/doc/config-default-template.md)
|
||||
3. [D:\dev\cmr-mini\event\classic-sequential.json](D:/dev/cmr-mini/event/classic-sequential.json)
|
||||
4. [D:\dev\cmr-mini\event\score-o.json](D:/dev/cmr-mini/event/score-o.json)
|
||||
|
||||
这样可以保证:
|
||||
|
||||
- 客户端实现
|
||||
- 服务端配置
|
||||
- 后台录入
|
||||
- 联调样例
|
||||
|
||||
始终保持一致。
|
||||
|
||||
|
||||
414
doc/archive/experience/H5体验接入方案.md
Normal file
414
doc/archive/experience/H5体验接入方案.md
Normal file
@@ -0,0 +1,414 @@
|
||||
# H5 体验接入方案
|
||||
|
||||
本文档用于定义当前项目中 **原生小程序 + H5 定制内容** 的混合接入方案。
|
||||
|
||||
目标:
|
||||
|
||||
- 保留原生地图与实时游戏主流程
|
||||
- 把高频变化、强定制的内容页交给 H5
|
||||
- 保证 H5 失败时,原生仍可完整兜底
|
||||
- 为后续客户定制、品牌包装、互动任务扩展留出稳定接口
|
||||
|
||||
---
|
||||
|
||||
## 1. 结论
|
||||
|
||||
当前最合适的方向不是“所有定制都 H5 化”,而是:
|
||||
|
||||
**原生负责核心游戏层,H5 负责定制体验层。**
|
||||
|
||||
也就是:
|
||||
|
||||
- 地图、打点、GPS、指北针、HUD、规则状态机继续原生
|
||||
- 结算页、文创详情、拍照任务、语音留言、小游戏、品牌包装页交给 H5
|
||||
|
||||
---
|
||||
|
||||
## 2. 适合 H5 化的内容
|
||||
|
||||
当前最适合 H5 承接的是:
|
||||
|
||||
- 结算页
|
||||
- 打点后的定制**详情页/互动页**
|
||||
- 文创详情页
|
||||
- 活动品牌页
|
||||
- 富图文任务页
|
||||
- 拍照上传 / 语音留言 / 小游戏类互动页
|
||||
- 表单、问卷、抽奖、作品提交页
|
||||
|
||||
不建议 H5 化的部分:
|
||||
|
||||
- 地图主界面
|
||||
- 打点逻辑
|
||||
- 自动转图
|
||||
- 指北针
|
||||
- HUD
|
||||
- GPS / 心率等实时能力主链
|
||||
- 需要强实时状态同步的高频游戏弹层
|
||||
- 游戏中的即时原生内容弹窗
|
||||
|
||||
一句话:
|
||||
|
||||
**核心实时游戏层保留原生,变化快的定制内容层交给 H5。**
|
||||
|
||||
---
|
||||
|
||||
## 2.1 当前阶段的定案
|
||||
|
||||
经过真机验证,当前项目已经明确:
|
||||
|
||||
- 小程序 `web-view` 在企业主体环境下可以正常打开
|
||||
- 但它不适合作为“原生弹窗里的局部 H5 内容区”使用
|
||||
- 真机上更接近整页原生容器,局部裁切、壳子覆盖、原生关闭按钮都不稳定
|
||||
|
||||
因此当前正式定案为:
|
||||
|
||||
- **打点后的即时内容:原生内容卡**
|
||||
- **H5:只作为详情页 / 互动任务页 / 全屏结果页**
|
||||
|
||||
也就是说:
|
||||
|
||||
- `content popup` 继续原生
|
||||
- 原生内容卡上提供 `查看详情`
|
||||
- 点 `查看详情` 后再进入 H5
|
||||
- H5 打不开时,原生内容卡继续兜底
|
||||
|
||||
---
|
||||
|
||||
## 3. 总体架构
|
||||
|
||||
推荐分成三层:
|
||||
|
||||
### 3.1 原生层
|
||||
|
||||
职责:
|
||||
|
||||
- 地图与渲染
|
||||
- GPS / 指北针 / 自动转图
|
||||
- 打点状态机
|
||||
- HUD
|
||||
- 心率 / telemetry
|
||||
- 原生内容卡兜底
|
||||
- 原生结果页兜底
|
||||
- 核心状态与本地缓存
|
||||
|
||||
### 3.2 H5 体验层
|
||||
|
||||
职责:
|
||||
|
||||
- 定制内容展示
|
||||
- 品牌包装
|
||||
- 富交互任务
|
||||
- 定制结算页
|
||||
- 富图文与媒体内容
|
||||
|
||||
### 3.3 Bridge 层
|
||||
|
||||
职责:
|
||||
|
||||
- 原生向 H5 注入上下文
|
||||
- H5 向原生请求能力
|
||||
- H5 把结果回传原生
|
||||
|
||||
---
|
||||
|
||||
## 4. 两种 H5 页面类型
|
||||
|
||||
### 4.1 Content Experience Page
|
||||
|
||||
用于游戏中途的**详情体验页**或**互动任务页**。
|
||||
|
||||
典型场景:
|
||||
|
||||
- 控制点打卡后点击 `查看详情`
|
||||
- 控制点点击后进入图文详情页
|
||||
- 拍照上传任务
|
||||
- 语音留言任务
|
||||
- 小游戏互动页
|
||||
- 问答/表单类互动页
|
||||
|
||||
### 4.2 Result Experience Page
|
||||
|
||||
用于游戏结束后的定制结算页。
|
||||
|
||||
典型场景:
|
||||
|
||||
- 活动定制结算
|
||||
- 奖章 / 解锁内容
|
||||
- 排名 / 分享
|
||||
- 作品提交
|
||||
- 品牌化结束页
|
||||
|
||||
---
|
||||
|
||||
## 5. 原生兜底原则
|
||||
|
||||
这是最重要的约束。
|
||||
|
||||
### 原则 1:核心流程先在原生完成
|
||||
|
||||
例如:
|
||||
|
||||
- 打点成功必须先由原生确认
|
||||
- 比赛结束必须先由原生确认
|
||||
- H5 只是附加体验,不拥有核心状态
|
||||
- 原生内容卡必须先可独立工作
|
||||
|
||||
### 原则 2:H5 打不开时回退原生
|
||||
|
||||
如果:
|
||||
|
||||
- 网络失败
|
||||
- H5 地址失效
|
||||
- 加载超时
|
||||
- Bridge 初始化失败
|
||||
|
||||
则直接回退:
|
||||
|
||||
- 原生内容卡
|
||||
- 原生结果页
|
||||
|
||||
### 原则 3:H5 不控制比赛状态
|
||||
|
||||
H5 可以展示、收集信息、提交任务结果。
|
||||
但不能决定:
|
||||
|
||||
- 是否打卡成功
|
||||
- 是否比赛完成
|
||||
- 是否跳点成功
|
||||
|
||||
这些只能由原生控制。
|
||||
|
||||
### 原则 4:H5 是可选增强,不是主流程依赖
|
||||
|
||||
即使 H5 没有打开:
|
||||
|
||||
- 游戏仍应可继续
|
||||
- 用户仍能完成路线
|
||||
- 用户仍能看到最小内容或最小结果
|
||||
|
||||
---
|
||||
|
||||
## 6. 配置模型建议
|
||||
|
||||
后续建议对“内容体验”和“结果体验”都支持两种类型:
|
||||
|
||||
- `native`
|
||||
- `h5`
|
||||
|
||||
### 6.1 内容体验配置示例
|
||||
|
||||
```json
|
||||
{
|
||||
"contentExperience": {
|
||||
"type": "h5",
|
||||
"url": "https://example.com/content/control-3",
|
||||
"bridge": "content-v1",
|
||||
"fallback": "native"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个字段当前应理解为:
|
||||
|
||||
- `contentExperience` = 原生内容卡上的 H5 **详情/互动扩展**
|
||||
- 不是直接顶替原生内容弹窗
|
||||
|
||||
或:
|
||||
|
||||
```json
|
||||
{
|
||||
"contentExperience": {
|
||||
"type": "native"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 结果页配置示例
|
||||
|
||||
```json
|
||||
{
|
||||
"resultExperience": {
|
||||
"type": "h5",
|
||||
"url": "https://example.com/result/score-o",
|
||||
"bridge": "result-v1",
|
||||
"fallback": "native"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 建议扩展字段
|
||||
|
||||
后续还可以逐步加入:
|
||||
|
||||
- `template`
|
||||
- `theme`
|
||||
- `timeoutMs`
|
||||
- `allowClose`
|
||||
- `prefetch`
|
||||
- `requiresNetwork`
|
||||
|
||||
---
|
||||
|
||||
## 7. 内容页与结果页的推荐职责
|
||||
|
||||
### 原生最小内容卡
|
||||
|
||||
负责:
|
||||
|
||||
- 最小图文说明
|
||||
- 最小点击查看
|
||||
- 自动弹出兜底
|
||||
|
||||
### H5 内容页
|
||||
|
||||
负责:
|
||||
|
||||
- 强样式定制
|
||||
- 多媒体内容
|
||||
- 任务型互动
|
||||
- 客户活动包装
|
||||
|
||||
### 原生最小结果页
|
||||
|
||||
负责:
|
||||
|
||||
- 结果一定可见
|
||||
- 成绩一定可回顾
|
||||
- 无网络也能展示基础结果
|
||||
|
||||
### H5 结果页
|
||||
|
||||
负责:
|
||||
|
||||
- 品牌化包装
|
||||
- 排名/分享
|
||||
- 作品提交
|
||||
- 奖章、解锁、收集册
|
||||
|
||||
---
|
||||
|
||||
## 8. 性能与体验要求
|
||||
|
||||
H5 接入时必须注意:
|
||||
|
||||
- 不阻塞原生主流程
|
||||
- 不把高频实时状态强行桥接到 H5
|
||||
- 不在地图进行中频繁开重页面
|
||||
- 低端机上优先简化交互和媒体资源
|
||||
|
||||
推荐策略:
|
||||
|
||||
- 内容详情页可以 H5
|
||||
- 地图中高频反馈继续原生
|
||||
- 结算增强页可以 H5
|
||||
- 结果最小摘要必须原生兜底
|
||||
|
||||
---
|
||||
|
||||
## 9. 当前建议实施顺序
|
||||
|
||||
### 第一步
|
||||
|
||||
先实现:
|
||||
|
||||
- 原生最小兜底内容卡
|
||||
- 原生最小结果页
|
||||
|
||||
### 第二步
|
||||
|
||||
新增一个通用 H5 容器页,用于承接:
|
||||
|
||||
- 内容页
|
||||
- 结果页
|
||||
|
||||
### 第三步
|
||||
|
||||
定义 Bridge 协议,并先支持最核心动作:
|
||||
|
||||
- 关闭
|
||||
- 获取上下文
|
||||
- 拍照
|
||||
- 录音
|
||||
- 提交结果
|
||||
|
||||
### 第四步
|
||||
|
||||
再让配置决定:
|
||||
|
||||
- 当前活动走原生
|
||||
- 还是走 H5
|
||||
|
||||
### 第五步
|
||||
|
||||
最后再逐步扩到:
|
||||
|
||||
- 上传能力
|
||||
- 分享能力
|
||||
- 小游戏任务
|
||||
- 作品提交
|
||||
|
||||
---
|
||||
|
||||
## 10. 下一步建议
|
||||
|
||||
当前最适合的下一步不是直接写复杂 H5 页面,而是:
|
||||
|
||||
1. 先定义原生与 H5 的统一入口模型
|
||||
2. 先把 Bridge 协议做小而稳
|
||||
3. 先做一个通用 H5 容器页
|
||||
4. 先让一个简单内容页或一个简单结果页跑通
|
||||
|
||||
---
|
||||
|
||||
## 11. 当前建议结论
|
||||
|
||||
最稳的方案不是“把定制内容都做成 H5”,而是:
|
||||
|
||||
**原生保底,H5 承接定制体验。**
|
||||
|
||||
这样既能支持客户高频变化需求,也不会破坏核心游戏体验。
|
||||
|
||||
---
|
||||
|
||||
## 12. 当前主体能力约束补充
|
||||
|
||||
最近实际排查已经确认:
|
||||
|
||||
- 当前最初使用的是**个人主体**小程序
|
||||
|
||||
在这个前提下,`web-view` 能力可能直接受到限制。
|
||||
这意味着:
|
||||
|
||||
- H5 页面本身可在浏览器打开
|
||||
- 小程序里仍然可能无法通过 `web-view` 打开
|
||||
|
||||
因此当前 H5 接入方案需要增加一个现实前提:
|
||||
|
||||
### 12.1 个人主体下
|
||||
|
||||
可以先做:
|
||||
|
||||
- 容器页
|
||||
- Bridge 协议
|
||||
- 配置结构
|
||||
- 原生兜底逻辑
|
||||
|
||||
但不要指望所有 H5 内容页都能在当前环境稳定跑通。
|
||||
|
||||
### 12.2 企业主体下
|
||||
|
||||
企业主体审核通过后,再优先回归:
|
||||
|
||||
- 最小 `web-view` 测试页
|
||||
- 内容体验页 H5
|
||||
- 结果页 H5
|
||||
|
||||
也就是说:
|
||||
|
||||
当前 H5 方案仍然成立,但在企业主体生效前,应按“预留 + 待验证”看待。
|
||||
|
||||
详细说明见:
|
||||
|
||||
- [platform-capability-notes.md](D:/dev/cmr-mini/doc/debug/平台能力说明.md)
|
||||
|
||||
233
doc/archive/experience/体验壳子方案.md
Normal file
233
doc/archive/experience/体验壳子方案.md
Normal file
@@ -0,0 +1,233 @@
|
||||
# Experience Shell 方案
|
||||
|
||||
本文档用于定义小程序中 H5 定制内容的承载方式。目标不是把 H5 做成真正的同页弹窗,而是做成:
|
||||
|
||||
- 独立页面路由
|
||||
- 原生壳子控制外观
|
||||
- `web-view` 只负责内容区
|
||||
|
||||
这样既保留了 H5 的定制能力,也能让用户感受更接近“弹窗”或“抽屉”。
|
||||
|
||||
---
|
||||
|
||||
## 1. 设计目标
|
||||
|
||||
当前 H5 内容页已经能打开,但整页全屏切换比较生硬,用户体验不够好。
|
||||
新的 `experience-shell` 目标是:
|
||||
|
||||
- 视觉上像弹窗
|
||||
- 保持原生关闭、回退、失败兜底逻辑
|
||||
- 不把地图主页面和 `web-view` 强绑在一起
|
||||
- 为后续结果页 H5、文创内容 H5 复用
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心原则
|
||||
|
||||
### 2.1 不做真正同页 H5 弹窗
|
||||
|
||||
微信小程序里的 `web-view` 更适合放在独立页面中承载。
|
||||
不要尝试把 `web-view` 直接叠在地图页上方做真弹窗,否则后续很容易遇到:
|
||||
|
||||
- 层级冲突
|
||||
- 手势冲突
|
||||
- iOS / Android 表现不一致
|
||||
- 遮罩和关闭逻辑变脏
|
||||
|
||||
### 2.2 原生壳子 + H5 内容区
|
||||
|
||||
最终结构应该是:
|
||||
|
||||
- 原生遮罩
|
||||
- 原生标题栏
|
||||
- 原生关闭按钮
|
||||
- `web-view` 内容区
|
||||
|
||||
也就是:
|
||||
|
||||
```text
|
||||
experience-shell
|
||||
├─ backdrop
|
||||
├─ native header
|
||||
└─ web-view body
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 支持的展示方式
|
||||
|
||||
第一阶段只支持 3 种:
|
||||
|
||||
- `sheet`
|
||||
- `dialog`
|
||||
- `fullscreen`
|
||||
|
||||
### 3.1 `sheet`
|
||||
|
||||
适合:
|
||||
|
||||
- 打点后的文创内容
|
||||
- 拍照任务
|
||||
- 轻互动内容
|
||||
|
||||
视觉:
|
||||
|
||||
- 自底部升起
|
||||
- 圆角卡片
|
||||
- 半透明暗背景
|
||||
|
||||
### 3.2 `dialog`
|
||||
|
||||
适合:
|
||||
|
||||
- 结果页
|
||||
- 中短内容
|
||||
- 重要说明
|
||||
|
||||
视觉:
|
||||
|
||||
- 居中大卡片
|
||||
- 更聚焦
|
||||
|
||||
### 3.3 `fullscreen`
|
||||
|
||||
适合:
|
||||
|
||||
- 长内容
|
||||
- 强定制专题页
|
||||
- 复杂表单/小游戏
|
||||
|
||||
---
|
||||
|
||||
## 4. 配置结构
|
||||
|
||||
H5 内容配置建议支持:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "h5",
|
||||
"url": "https://example.com/content/control-1",
|
||||
"bridge": "content-v1",
|
||||
"presentation": "sheet"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
- `type`
|
||||
当前支持 `native` / `h5`
|
||||
- `url`
|
||||
H5 页面地址
|
||||
- `bridge`
|
||||
bridge 版本
|
||||
- `presentation`
|
||||
展示方式,支持:
|
||||
- `sheet`
|
||||
- `dialog`
|
||||
- `fullscreen`
|
||||
|
||||
默认值建议:
|
||||
|
||||
- 内容体验默认 `sheet`
|
||||
- 结果页默认 `dialog`
|
||||
|
||||
---
|
||||
|
||||
## 5. 原生壳子职责
|
||||
|
||||
原生壳子负责:
|
||||
|
||||
- 遮罩
|
||||
- 标题、副标题
|
||||
- 关闭按钮
|
||||
- 页面进入/退出动画
|
||||
- H5 打开失败回退
|
||||
|
||||
原生壳子不负责:
|
||||
|
||||
- H5 页面内部业务逻辑
|
||||
- H5 具体视觉排版
|
||||
|
||||
---
|
||||
|
||||
## 6. 关闭与回退逻辑
|
||||
|
||||
### 6.1 原生关闭
|
||||
|
||||
原生必须始终支持:
|
||||
|
||||
- 右上/头部关闭
|
||||
- 返回键关闭
|
||||
- 失败时自动关闭并回退
|
||||
|
||||
### 6.2 H5 请求关闭
|
||||
|
||||
H5 可以通过 bridge 发:
|
||||
|
||||
- `close`
|
||||
|
||||
然后由原生统一关闭壳子页。
|
||||
|
||||
### 6.3 H5 失败回退
|
||||
|
||||
如果出现:
|
||||
|
||||
- URL 无效
|
||||
- 页面打不开
|
||||
- bridge 初始化失败
|
||||
|
||||
统一回退到:
|
||||
|
||||
- 原生内容卡
|
||||
- 原生结果页
|
||||
|
||||
---
|
||||
|
||||
## 7. 动画建议
|
||||
|
||||
### `sheet`
|
||||
|
||||
- 遮罩淡入
|
||||
- 面板自下而上出现
|
||||
|
||||
### `dialog`
|
||||
|
||||
- 遮罩淡入
|
||||
- 面板轻微放大进入
|
||||
|
||||
### `lite`
|
||||
|
||||
在低端机或 `lite` 模式下:
|
||||
|
||||
- 只保留 opacity
|
||||
- 降低位移动画强度
|
||||
|
||||
---
|
||||
|
||||
## 8. 推荐接入顺序
|
||||
|
||||
### 第一阶段
|
||||
|
||||
- 先把当前 `experience-webview` 升级成 shell
|
||||
- 先支持 `sheet`
|
||||
- 先接 `content-v1`
|
||||
|
||||
### 第二阶段
|
||||
|
||||
- 补 `dialog`
|
||||
- 结果页 H5 开始复用壳子
|
||||
|
||||
### 第三阶段
|
||||
|
||||
- 主题样式可配置
|
||||
- 过场动画接入
|
||||
|
||||
---
|
||||
|
||||
## 9. 一句话结论
|
||||
|
||||
小程序里的 H5 不应该直接作为“生硬全页”使用,也不应该强行做成“地图页上的真弹窗”。
|
||||
最稳的方案是:
|
||||
|
||||
**独立页面承载,但由原生壳子把它做成 `sheet / dialog / fullscreen` 三种体验形态。**
|
||||
|
||||
329
doc/archive/experience/内容体验层方案.md
Normal file
329
doc/archive/experience/内容体验层方案.md
Normal file
@@ -0,0 +1,329 @@
|
||||
# 游戏中文创体验层方案
|
||||
|
||||
## 1. 目标
|
||||
|
||||
为游戏过程中的文创内容建立一层独立承载能力,不把内容弹窗、图文卡片、讲解信息散落在:
|
||||
|
||||
- 规则层
|
||||
- 页面层
|
||||
- HUD 逻辑
|
||||
- 反馈层
|
||||
|
||||
这层的目标是:
|
||||
|
||||
- 在正确时机触发内容体验
|
||||
- 统一内容展示方式
|
||||
- 可配置、可复用、可扩展
|
||||
- 不破坏当前地图与规则主链
|
||||
|
||||
一句话:
|
||||
|
||||
**把“中途内容体验”从临时弹窗提升为正式能力层。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前现状
|
||||
|
||||
当前项目已经具备一部分基础:
|
||||
|
||||
- `control.displayContent`
|
||||
- `UiEffectDirector.showContentCard(...)`
|
||||
- 页面层已有 `contentCardVisible / contentCardTitle / contentCardBody`
|
||||
- 打点完成后可展示内容卡
|
||||
|
||||
这说明:
|
||||
|
||||
- 内容展示能力已经有雏形
|
||||
- 但触发方式还偏单一
|
||||
- 内容形式也还比较轻
|
||||
- 还没有形成正式的“内容体验层”模型
|
||||
|
||||
---
|
||||
|
||||
## 3. 设计原则
|
||||
|
||||
### 3.1 内容体验不等于短反馈
|
||||
|
||||
短反馈仍然属于:
|
||||
|
||||
- 音效
|
||||
- 震动
|
||||
- HUD 提示
|
||||
- 地图 pulse
|
||||
|
||||
文创体验属于更重的一层,应与 `FeedbackDirector` 区分。
|
||||
|
||||
### 3.2 内容体验不直接写死在规则里
|
||||
|
||||
规则层只负责:
|
||||
|
||||
- 是否触发
|
||||
- 触发什么体验条目
|
||||
|
||||
规则层不负责:
|
||||
|
||||
- 页面怎么弹
|
||||
- 卡片长什么样
|
||||
- 是否带图片、音频、讲解按钮
|
||||
|
||||
### 3.3 内容体验必须配置驱动
|
||||
|
||||
以后不同活动、不同地图、不同玩法需要不同内容。
|
||||
|
||||
所以这层必须可配置:
|
||||
|
||||
- 哪个点触发
|
||||
- 何时触发
|
||||
- 弹什么
|
||||
- 是否只弹一次
|
||||
- 优先级如何
|
||||
|
||||
---
|
||||
|
||||
## 4. 建议的新层级
|
||||
|
||||
建议增加一层:
|
||||
|
||||
- `ContentExperienceLayer`
|
||||
|
||||
放在概念上与这些层并列:
|
||||
|
||||
- `MapPresentation`
|
||||
- `HUD`
|
||||
- `Feedback`
|
||||
- `ResultScene`
|
||||
|
||||
职责:
|
||||
|
||||
- 接收体验触发
|
||||
- 管理当前激活内容项
|
||||
- 控制展示与关闭
|
||||
- 向页面层输出当前体验模型
|
||||
|
||||
---
|
||||
|
||||
## 5. 建议的数据模型
|
||||
|
||||
### 5.1 ExperienceEntry
|
||||
|
||||
```ts
|
||||
type ExperienceTrigger =
|
||||
| 'control_completed'
|
||||
| 'zone_entered'
|
||||
| 'session_finished'
|
||||
| 'manual'
|
||||
|
||||
type ExperienceDisplayMode =
|
||||
| 'content-card'
|
||||
| 'full-panel'
|
||||
| 'audio-guide'
|
||||
| 'unlock-card'
|
||||
|
||||
interface ExperienceEntry {
|
||||
id: string
|
||||
trigger: ExperienceTrigger
|
||||
controlId?: string
|
||||
zoneId?: string
|
||||
title: string
|
||||
body: string
|
||||
imageRef?: string
|
||||
audioRef?: string
|
||||
displayMode: ExperienceDisplayMode
|
||||
once: boolean
|
||||
priority: number
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 ExperienceRuntimeState
|
||||
|
||||
```ts
|
||||
interface ExperienceRuntimeState {
|
||||
activeEntryId: string | null
|
||||
dismissedEntryIds: string[]
|
||||
consumedEntryIds: string[]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 配置建议
|
||||
|
||||
建议在配置中增加一段:
|
||||
|
||||
```json
|
||||
{
|
||||
"resources": {
|
||||
"contentEntries": {
|
||||
"cp-3-story": {
|
||||
"title": "校史地标",
|
||||
"body": "这里是校园历史演变的重要节点。",
|
||||
"imageRef": "content/campus-history-01.png",
|
||||
"displayMode": "content-card"
|
||||
}
|
||||
}
|
||||
},
|
||||
"game": {
|
||||
"experience": {
|
||||
"entries": [
|
||||
{
|
||||
"id": "cp-3-story",
|
||||
"trigger": "control_completed",
|
||||
"controlId": "control-3",
|
||||
"once": true,
|
||||
"priority": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这意味着:
|
||||
|
||||
- 资源层管理内容资源
|
||||
- 玩法配置决定何时触发
|
||||
|
||||
---
|
||||
|
||||
## 7. 触发来源
|
||||
|
||||
第一阶段建议支持 3 种触发:
|
||||
|
||||
### 7.1 打点完成触发
|
||||
|
||||
最适合当前项目,价值最高。
|
||||
|
||||
例如:
|
||||
|
||||
- 完成某个控制点后弹一张文创卡
|
||||
- 开始点完成后弹赛事导览卡
|
||||
- 终点完成后弹纪念卡
|
||||
|
||||
### 7.2 区域进入触发
|
||||
|
||||
适合后续:
|
||||
|
||||
- 地标介绍
|
||||
- 迷雾探索
|
||||
- 特定区域故事点
|
||||
|
||||
### 7.3 结算后解锁触发
|
||||
|
||||
适合后续与结算页联动:
|
||||
|
||||
- 收藏卡
|
||||
- 奖章
|
||||
- 文创奖励
|
||||
|
||||
---
|
||||
|
||||
## 8. 页面表现建议
|
||||
|
||||
第一阶段先做最小闭环,不追求复杂视觉。
|
||||
|
||||
### 8.1 第一阶段
|
||||
|
||||
支持:
|
||||
|
||||
- 当前已有的 `content-card`
|
||||
- 标题
|
||||
- 正文
|
||||
- 关闭
|
||||
|
||||
### 8.2 第二阶段
|
||||
|
||||
再支持:
|
||||
|
||||
- 图片
|
||||
- 按钮
|
||||
- 章节式展开
|
||||
- 音频讲解
|
||||
|
||||
---
|
||||
|
||||
## 9. 与当前架构的关系
|
||||
|
||||
### 规则层
|
||||
|
||||
负责:
|
||||
|
||||
- 触发某条体验事件
|
||||
|
||||
不负责:
|
||||
|
||||
- 具体展示细节
|
||||
|
||||
### Feedback
|
||||
|
||||
继续负责:
|
||||
|
||||
- 短反馈
|
||||
- 动效
|
||||
- 音效
|
||||
|
||||
### ContentExperienceLayer
|
||||
|
||||
负责:
|
||||
|
||||
- 中等时长的信息体验
|
||||
|
||||
### 页面层
|
||||
|
||||
负责:
|
||||
|
||||
- 渲染当前体验模型
|
||||
|
||||
---
|
||||
|
||||
## 10. 第一阶段最小实施范围
|
||||
|
||||
建议第一阶段只做:
|
||||
|
||||
1. `control_completed -> experience entry`
|
||||
2. `content-card` 展示
|
||||
3. `once` 语义
|
||||
4. 手动关闭
|
||||
5. 配置驱动
|
||||
|
||||
不要一上来做:
|
||||
|
||||
- 图片轮播
|
||||
- 视频
|
||||
- 复杂音频控制
|
||||
- 多层交互
|
||||
|
||||
---
|
||||
|
||||
## 11. 推荐实施顺序
|
||||
|
||||
1. 定义 `ExperienceEntry`
|
||||
2. 在配置解析层接 `game.experience.entries`
|
||||
3. 在规则完成事件里派发体验触发
|
||||
4. MapEngine 增加体验状态承载
|
||||
5. 页面层继续复用当前 `content-card`
|
||||
6. 再逐步升级 UI
|
||||
|
||||
---
|
||||
|
||||
## 12. 长期价值
|
||||
|
||||
这层建好后,后续可以自然承接:
|
||||
|
||||
- 文创卡片
|
||||
- 地标解说
|
||||
- 解锁收藏
|
||||
- 故事节点
|
||||
- 活动内品牌内容
|
||||
|
||||
它不只服务当前顺序赛/积分赛,而是服务整条产品体验链。
|
||||
|
||||
---
|
||||
|
||||
## 13. 结论
|
||||
|
||||
当前最正确的方向不是继续在页面里零散补内容弹窗,而是:
|
||||
|
||||
**把游戏中途的文创与故事体验正式抽成一层独立的 `ContentExperienceLayer`。**
|
||||
|
||||
第一阶段先用“控制点完成触发内容卡”跑通最小闭环,后面再逐步扩成完整体验系统。
|
||||
|
||||
294
doc/archive/experience/结果页方案.md
Normal file
294
doc/archive/experience/结果页方案.md
Normal file
@@ -0,0 +1,294 @@
|
||||
# 游戏结算层方案
|
||||
|
||||
## 1. 目标
|
||||
|
||||
为游戏结束后的结果展示建立独立结算层,不把结算逻辑散落在:
|
||||
|
||||
- 规则层
|
||||
- HUD
|
||||
- 顶部提示
|
||||
- 页面临时弹窗
|
||||
|
||||
目标是:
|
||||
|
||||
- 统一承接结束态
|
||||
- 展示成绩与摘要信息
|
||||
- 支撑不同玩法的结算差异
|
||||
- 为后续文创奖励、奖章、分享做扩展位
|
||||
|
||||
一句话:
|
||||
|
||||
**把“比赛结束后显示点什么”提升为正式的结果场景能力。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前现状
|
||||
|
||||
当前项目已经有:
|
||||
|
||||
- `session_finished`
|
||||
- `gameSessionStatus = finished`
|
||||
- 基础成绩、里程、时长、心率等 telemetry
|
||||
- 游戏信息面板可读取当前状态快照
|
||||
|
||||
但还没有正式的:
|
||||
|
||||
- `ResultScene`
|
||||
- `SummaryModel`
|
||||
- 结束后专属页面承载
|
||||
|
||||
---
|
||||
|
||||
## 3. 设计原则
|
||||
|
||||
### 3.1 结算不应只是提示条
|
||||
|
||||
结束不是一个瞬时反馈,而是一次阶段切换。
|
||||
|
||||
所以它需要独立层,而不是只弹一句:
|
||||
|
||||
- 已完成
|
||||
- 已结束
|
||||
|
||||
### 3.2 结算要与玩法解耦
|
||||
|
||||
顺序赛、积分赛、后续幽灵赛、金币赛,结算内容不同。
|
||||
|
||||
所以应该有:
|
||||
|
||||
- 通用结算结构
|
||||
- 玩法补充区块
|
||||
|
||||
### 3.3 结算要可扩
|
||||
|
||||
后续可能加入:
|
||||
|
||||
- 奖章
|
||||
- 排名
|
||||
- 收藏卡
|
||||
- 文创解锁
|
||||
- 分享图
|
||||
|
||||
所以一开始就要留结构。
|
||||
|
||||
---
|
||||
|
||||
## 4. 建议的新层级
|
||||
|
||||
建议增加:
|
||||
|
||||
- `ResultScene`
|
||||
|
||||
概念上与这些层并列:
|
||||
|
||||
- `MapPresentation`
|
||||
- `HUD`
|
||||
- `Feedback`
|
||||
- `ContentExperienceLayer`
|
||||
|
||||
职责:
|
||||
|
||||
- 承接结束态
|
||||
- 持有结算模型
|
||||
- 控制显示与关闭
|
||||
- 为玩法结果提供统一展示结构
|
||||
|
||||
---
|
||||
|
||||
## 5. 建议的数据模型
|
||||
|
||||
### 5.1 SummaryModel
|
||||
|
||||
```ts
|
||||
interface ResultSummaryModel {
|
||||
title: string
|
||||
subtitle: string
|
||||
mode: string
|
||||
finished: boolean
|
||||
durationMs: number
|
||||
distanceMeters: number
|
||||
averageSpeedKmh: number | null
|
||||
calories: number | null
|
||||
averageHeartRateBpm: number | null
|
||||
completedCount: number
|
||||
skippedCount: number
|
||||
totalCount: number
|
||||
score: number | null
|
||||
extraRows: Array<{ label: string; value: string }>
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 ResultSceneState
|
||||
|
||||
```ts
|
||||
interface ResultSceneState {
|
||||
visible: boolean
|
||||
summary: ResultSummaryModel | null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 第一阶段应展示什么
|
||||
|
||||
建议先做一版“基础结算页”,不要一上来做复杂演出。
|
||||
|
||||
### 通用区域
|
||||
|
||||
- 赛事名称
|
||||
- 玩法名称
|
||||
- 完成状态
|
||||
- 总用时
|
||||
- 总里程
|
||||
- 平均速度
|
||||
- 卡路里
|
||||
- 平均心率
|
||||
|
||||
### 玩法区域
|
||||
|
||||
顺序赛:
|
||||
|
||||
- 完成控制点数量
|
||||
- 跳过点数量
|
||||
- 总控制点数量
|
||||
|
||||
积分赛:
|
||||
|
||||
- 总得分
|
||||
- 已完成点数
|
||||
- 未完成点数
|
||||
|
||||
### 操作区
|
||||
|
||||
- 返回地图
|
||||
- 关闭
|
||||
- 后续再加重开 / 分享
|
||||
|
||||
---
|
||||
|
||||
## 7. 配置建议
|
||||
|
||||
建议在配置中预留:
|
||||
|
||||
```json
|
||||
{
|
||||
"game": {
|
||||
"result": {
|
||||
"enabled": true,
|
||||
"showTelemetry": true,
|
||||
"showCollectedContent": true,
|
||||
"showAwards": false,
|
||||
"template": "default"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这意味着:
|
||||
|
||||
- 结算是否启用
|
||||
- 展示哪些区块
|
||||
- 用哪个模板
|
||||
|
||||
都可配置。
|
||||
|
||||
---
|
||||
|
||||
## 8. 与当前架构的关系
|
||||
|
||||
### 规则层
|
||||
|
||||
负责:
|
||||
|
||||
- 产出 `session_finished`
|
||||
|
||||
### Telemetry
|
||||
|
||||
负责:
|
||||
|
||||
- 提供里程、速度、心率、卡路里等数据
|
||||
|
||||
### MapEngine
|
||||
|
||||
负责:
|
||||
|
||||
- 在结束时汇总通用结算模型
|
||||
- 把结果快照送到页面层
|
||||
|
||||
### 页面层
|
||||
|
||||
负责:
|
||||
|
||||
- 渲染结算页
|
||||
|
||||
---
|
||||
|
||||
## 9. 第一阶段最小实施范围
|
||||
|
||||
建议第一阶段只做:
|
||||
|
||||
1. `session_finished -> ResultScene`
|
||||
2. 基础 summary 展示
|
||||
3. 顺序赛 / 积分赛的简单差异化字段
|
||||
4. 手动关闭 / 返回地图
|
||||
|
||||
先不要一上来做:
|
||||
|
||||
- 复杂章节动画
|
||||
- 排名
|
||||
- 分享图生成
|
||||
- 复杂奖章系统
|
||||
|
||||
---
|
||||
|
||||
## 10. 后续扩展方向
|
||||
|
||||
这层建好后,可以逐步加:
|
||||
|
||||
- 文创奖励
|
||||
- 奖章 / 成就
|
||||
- 排名
|
||||
- 解锁内容
|
||||
- 分享卡
|
||||
- 二次引导
|
||||
|
||||
---
|
||||
|
||||
## 11. 推荐实施顺序
|
||||
|
||||
1. 定义 `ResultSummaryModel`
|
||||
2. 在 `MapEngine` 汇总结束快照
|
||||
3. 页面层增加结果面板
|
||||
4. 顺序赛 / 积分赛各补一组玩法字段
|
||||
5. 再考虑动画、奖励和品牌内容
|
||||
|
||||
---
|
||||
|
||||
## 12. 与文创体验层的配合
|
||||
|
||||
后续建议:
|
||||
|
||||
- 文创体验层
|
||||
- 承接“游戏中途”的体验
|
||||
- 结算层
|
||||
- 承接“游戏结束后”的体验
|
||||
|
||||
二者不要混。
|
||||
|
||||
如果后续结算后要解锁文创卡片,可以由:
|
||||
|
||||
- `ResultScene`
|
||||
- 显示结算
|
||||
- 结算完成后
|
||||
- 再触发内容奖励卡
|
||||
|
||||
---
|
||||
|
||||
## 13. 结论
|
||||
|
||||
当前最合适的方向不是继续在结束时零散堆文案,而是:
|
||||
|
||||
**正式增加一层 `ResultScene`,承接顺序赛、积分赛以及未来更多玩法的统一结算体验。**
|
||||
|
||||
第一阶段先做基础 summary,后续再逐步接入文创奖励、奖章、排名和过场动画。
|
||||
|
||||
51
doc/archive/notes/Gemini分析.md
Normal file
51
doc/archive/notes/Gemini分析.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# CMR-Mini 项目深度分析报告 (GeminiAnalysis.md)
|
||||
|
||||
## 1. 项目定位与核心愿景
|
||||
**CMR-Mini** 是一个运行在微信小程序环境中的高性能**定向越野 (Orienteering)** 实时竞赛/练习引擎。其核心竞争力在于通过自研的 **WebGL 地图渲染管线** 提供流畅的地图交互,并结合高精度多传感器融合技术(GPS、罗盘、心率、加速度计等)实现精准的运动反馈。
|
||||
|
||||
## 2. 核心系统架构分析
|
||||
|
||||
### 2.1 地图渲染引擎 (Map Engine)
|
||||
* **渲染技术**:采用 `Single WebGL Pipeline`。相比微信原生地图组件,具有更高的定制化能力,特别是在“Heading-Up”(朝向朝上)模式下的性能表现。
|
||||
* **瓦片管理**:通过 `TileStore` 实现三级缓存(内存 -> 磁盘 -> 网络),并支持 `tilePersistentCache`。
|
||||
* **投影逻辑**:采用 `WGS84 -> WorldTile -> Camera -> Screen` 的标准 GIS 变换链,能够精准处理地理坐标到屏幕像素的映射。
|
||||
|
||||
### 2.2 传感器融合系统 (Sensor System)
|
||||
* **CompassHeadingController**:核心逻辑在于罗盘数据 (`wx.onCompassChange`) 与设备姿态 (`wx.onDeviceMotionChange`) 的协同。
|
||||
* **LocationController**:支持真实 GPS 数据与 Mock 模拟器(通过 WebSocket 连接 `mock-gps-sim` 工具)的无缝切换。
|
||||
* **TelemetryRuntime**:实现了运动参数的实时计算,包括速度、距离目标点距离、心率分区等指标。
|
||||
|
||||
### 2.3 游戏逻辑与规则 (Game Logic)
|
||||
* **GameRuntime**:驱动对局状态机,支持“顺序赛 (Classic Sequential)”与“积分赛 (Score-O)”。
|
||||
* **PunchPolicy**:实现了自动进入检查点范围触发、手动打点、跳过点位等业务逻辑。
|
||||
|
||||
## 3. 指北针 (Compass) 平滑度瓶颈分析
|
||||
根据目前的实现,指北针的卡顿感主要源于以下三个层面:
|
||||
|
||||
1. **采样频率与插值逻辑**:
|
||||
* 目前使用 `interpolateHeadingDeg` 进行线性差值,且 `ABSOLUTE_HEADING_CORRECTION` 为固定系数 (0.44)。这种静态系数在“静态微调”时显得不够敏锐,在“快速旋转”时又显得滞后。
|
||||
2. **Android/iOS 差异化丢帧**:
|
||||
* Android 传感器回调频率不稳定。
|
||||
* 逻辑中对 `direction` 进行了严格的数值有效性判断,若系统由于硬件抖动返回短时异常值,会导致视觉上的“跳帧”。
|
||||
3. **UI 同步周期限制**:
|
||||
* `MapEngine` 的 `UI_SYNC_INTERVAL_MS` 设置为 80ms,这意味着视觉反馈的最高帧率仅为 12.5Hz,远低于屏幕刷新率,导致指针转动不够丝滑。
|
||||
|
||||
## 4. 优化技术路线建议
|
||||
|
||||
### 4.1 引入指数加权移动平均 (EWMA) 的动态系数
|
||||
建议根据旋转角速度动态调整平滑系数。当检测到瞬时角位移较大时,降低平滑度以追求响应速度;当位移较小时,增加平滑度以过滤手抖带来的噪声。
|
||||
|
||||
### 4.2 视觉平滑:使用 CSS Transform 或 WebGL 帧间补偿
|
||||
目前数据是由控制器下发到 UI 的。建议:
|
||||
* **方案 A (推荐)**:在 UI 层(`.wxml`/`.wxss`)利用 `transition: transform 0.1s linear;` 实现视觉层面的自动补帧。
|
||||
* **方案 B**:在 WebGL 渲染循环内进行帧间插值,将数据的 12.5Hz 提升到 渲染循环的 60Hz。
|
||||
|
||||
### 4.3 预测与死区 (Dead-zone) 过滤
|
||||
在 `CompassHeadingController` 中加入微小位移的死区过滤逻辑,避免由于硬件高频微小抖动导致的视图高频重绘,降低系统功耗的同时提升视觉稳定性。
|
||||
|
||||
## 5. 结论
|
||||
CMR-Mini 已经建立了一个非常坚实的专业定向越野引擎基础。后续的优化重点应从“功能的实现”转向“交互的极致平滑”,特别是针对指北针这类核心导向组件,需要更精细化的信号处理策略。
|
||||
|
||||
---
|
||||
*Generated by Gemini CLI Analysis Tool*
|
||||
|
||||
210
doc/archive/notes/临时玩法讨论.md
Normal file
210
doc/archive/notes/临时玩法讨论.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# 临时玩法讨论记录
|
||||
|
||||
本文档用于临时记录以下讨论内容:
|
||||
|
||||
- 贪吃蛇式玩法是否适配当前架构
|
||||
- 超级玛丽拾金币式玩法是否适配当前架构
|
||||
- 这些玩法是否需要大改现有系统
|
||||
|
||||
当前结论仅用于阶段讨论,不作为正式设计冻结文档。
|
||||
|
||||
---
|
||||
|
||||
## 1. 结论
|
||||
|
||||
当前这两类玩法都适合现有架构。
|
||||
|
||||
- `贪吃蛇式玩法`:适合
|
||||
- `区域拾金币玩法`:适合
|
||||
- 二者都不需要推翻现有主架构
|
||||
- 主要工作会集中在:
|
||||
- 新的 `RulePlugin`
|
||||
- 新的 `modeState`
|
||||
- 新的 `map/hud presentation`
|
||||
- 少量内容模型扩展
|
||||
|
||||
也就是说,这两类玩法更像是在现有底座上继续长新玩法,而不是重做底层。
|
||||
|
||||
---
|
||||
|
||||
## 2. 为什么适合当前架构
|
||||
|
||||
当前系统已经拆出了以下关键层:
|
||||
|
||||
- 地图引擎
|
||||
- 规则引擎
|
||||
- telemetry 信息层
|
||||
- map / hud presentation
|
||||
- feedback 反馈层
|
||||
- 真实 / 模拟传感输入
|
||||
|
||||
这意味着:
|
||||
|
||||
- 地图只负责显示和交互能力
|
||||
- 规则层只负责玩法推进
|
||||
- telemetry 只负责通用过程信息
|
||||
- feedback 只负责声音、震动、动效等效果消费
|
||||
|
||||
因此后续新增玩法,原则上主要是“新增规则和表现”,而不是“重写地图页”。
|
||||
|
||||
---
|
||||
|
||||
## 3. 贪吃蛇式玩法分析
|
||||
|
||||
### 3.1 玩法本质
|
||||
|
||||
这类玩法通常包含:
|
||||
|
||||
- 玩家位置持续更新
|
||||
- 轨迹形成蛇身
|
||||
- 尾巴按规则增长或收缩
|
||||
- 撞到自己、奖励点、危险区后触发状态变化
|
||||
|
||||
### 3.2 适配当前架构的原因
|
||||
|
||||
当前架构已经具备:
|
||||
|
||||
- 持续 GPS 输入
|
||||
- 持续 telemetry 更新
|
||||
- 规则事件驱动推进
|
||||
- 地图轨迹绘制能力
|
||||
- 统一反馈系统
|
||||
|
||||
因此它天然可以承载:
|
||||
|
||||
- 尾巴增长
|
||||
- 尾巴裁切
|
||||
- 自碰撞
|
||||
- 收集奖励
|
||||
- 危险区域
|
||||
|
||||
### 3.3 真正需要新增的内容
|
||||
|
||||
主要是玩法私有状态,而不是底层推翻:
|
||||
|
||||
- `snakeBody`
|
||||
- `tailLength`
|
||||
- `tailWindow`
|
||||
- `collisionState`
|
||||
- `collectibleState`
|
||||
|
||||
这些都应放入该玩法自己的 `modeState`。
|
||||
|
||||
### 3.4 对当前架构的压力点
|
||||
|
||||
这类玩法会推动当前系统继续增强:
|
||||
|
||||
- `modeState` 承载更复杂连续状态
|
||||
- `MapPresentation` 支持蛇身/危险区/奖励点等更多图元
|
||||
- 规则层处理持续碰撞判定
|
||||
|
||||
但这些属于增强,不属于重构。
|
||||
|
||||
---
|
||||
|
||||
## 4. 区域拾金币玩法分析
|
||||
|
||||
### 4.1 玩法本质
|
||||
|
||||
这类玩法通常包含:
|
||||
|
||||
- 玩家在某片区域内自由移动
|
||||
- 经过或进入范围后收集金币
|
||||
- 有时间限制、连击或区域目标
|
||||
- 可附带终点或出口点
|
||||
|
||||
### 4.2 适配当前架构的原因
|
||||
|
||||
它本质上非常接近:
|
||||
|
||||
- 自由收集
|
||||
- 多目标高亮
|
||||
- 局部 HUD 提示
|
||||
|
||||
而这些当前在 `score-o` 里已经有相当基础。
|
||||
|
||||
因此它可以看作:
|
||||
|
||||
- `score-o` 的泛化版
|
||||
- 或“自由收集类玩法”的一个子类
|
||||
|
||||
### 4.3 真正需要新增的内容
|
||||
|
||||
这类玩法一般需要:
|
||||
|
||||
- 新点位类型:`coin / pickup / bonus`
|
||||
- 新 HUD 信息:已收集数、剩余金币、区域完成度
|
||||
- 新表现:金币图标、收集动效、区域边界
|
||||
|
||||
### 4.4 对当前架构的压力点
|
||||
|
||||
这类玩法比蛇尾玩法对底座压力更小。
|
||||
|
||||
它主要会推动:
|
||||
|
||||
- 内容模型从“控制点”继续泛化
|
||||
- `MapPresentation` 支持更多点位类型
|
||||
- HUD 能容纳玩法专属信息
|
||||
|
||||
但依然不需要大改主架构。
|
||||
|
||||
---
|
||||
|
||||
## 5. 需要补强的底座点
|
||||
|
||||
如果未来真的开发这两类玩法,最值得继续补强的是:
|
||||
|
||||
- 更明确的 `modeState` 规范
|
||||
- 更强的 `MapPresentation`
|
||||
- 更通用的内容模型
|
||||
- 更清晰的玩法事件字典
|
||||
|
||||
建议后续逐步支持的通用对象类型:
|
||||
|
||||
- `control`
|
||||
- `collectible`
|
||||
- `bonus`
|
||||
- `hazard`
|
||||
- `trigger`
|
||||
- `zone`
|
||||
- `exit`
|
||||
|
||||
建议后续逐步支持的通用事件:
|
||||
|
||||
- `item_collected`
|
||||
- `zone_entered`
|
||||
- `zone_left`
|
||||
- `self_collision`
|
||||
- `combo_started`
|
||||
- `combo_broken`
|
||||
- `area_cleared`
|
||||
|
||||
---
|
||||
|
||||
## 6. 当前判断标准
|
||||
|
||||
如果未来实现这些玩法时出现以下现象,说明架构边界可能需要重审:
|
||||
|
||||
- 必须大改 `MapEngine`
|
||||
- 必须大改 `TelemetryRuntime`
|
||||
- 必须让渲染器自己猜玩法规则
|
||||
- 必须把玩法私有状态塞进全局 telemetry
|
||||
|
||||
如果没有出现这些情况,而主要只是新增:
|
||||
|
||||
- `RulePlugin`
|
||||
- `modeState`
|
||||
- `presentation`
|
||||
- `feedback`
|
||||
|
||||
那就说明当前架构是适配的。
|
||||
|
||||
---
|
||||
|
||||
## 7. 当前阶段总判断
|
||||
|
||||
结论可以总结成一句话:
|
||||
|
||||
当前这套架构不仅适合传统定向和积分赛,也适合继续承载更游戏化的运动玩法。
|
||||
像贪吃蛇式玩法和区域拾金币玩法,都更像是“新增玩法插件”,而不是“推翻现有底座”。
|
||||
|
||||
571
doc/archive/notes/传感器接入待办.md
Normal file
571
doc/archive/notes/传感器接入待办.md
Normal file
@@ -0,0 +1,571 @@
|
||||
# 传感器接入待开发方案
|
||||
|
||||
本文档用于整理当前项目后续可利用的传感器能力,分为:
|
||||
|
||||
- 微信小程序能力边界
|
||||
- 原生 Flutter App 能力边界
|
||||
- 两端统一的抽象建议
|
||||
- 推荐落地顺序
|
||||
|
||||
目标不是一次性接入所有传感器,而是优先接入对当前地图玩法、自动转图、运动状态识别、HUD/反馈最有价值的能力。
|
||||
|
||||
---
|
||||
|
||||
## 1. 总体原则
|
||||
|
||||
传感器接入必须遵守以下原则:
|
||||
|
||||
- 原始传感器数据只放在 `engine/sensor`
|
||||
- 融合后的高级状态放在 `telemetry`
|
||||
- 地图引擎只消费“对地图有意义的结果”
|
||||
- 规则引擎只在玩法确实需要时消费高级状态
|
||||
- 不要把原始三轴值直接喂给地图或玩法逻辑
|
||||
|
||||
推荐统一产出的高级状态包括:
|
||||
|
||||
- `movementState`
|
||||
- `headingSource`
|
||||
- `devicePose`
|
||||
- `headingConfidence`
|
||||
- `cadenceSpm`
|
||||
- `motionIntensity`
|
||||
|
||||
---
|
||||
|
||||
## 2. 微信小程序可用传感器
|
||||
|
||||
### 2.1 当前确认可用
|
||||
|
||||
基于微信小程序官方 API 与项目内 typings,当前可直接使用的能力包括:
|
||||
|
||||
- `Location`
|
||||
- `wx.startLocationUpdate`
|
||||
- `wx.startLocationUpdateBackground`
|
||||
- `wx.onLocationChange`
|
||||
- `Accelerometer`
|
||||
- `wx.startAccelerometer`
|
||||
- `wx.onAccelerometerChange`
|
||||
- `Compass`
|
||||
- `wx.startCompass`
|
||||
- `wx.onCompassChange`
|
||||
- `DeviceMotion`
|
||||
- `wx.startDeviceMotionListening`
|
||||
- `wx.onDeviceMotionChange`
|
||||
- `Gyroscope`
|
||||
- `wx.startGyroscope`
|
||||
- `wx.onGyroscopeChange`
|
||||
- `WeRunData`
|
||||
- `wx.getWeRunData`
|
||||
|
||||
### 2.2 当前确认不可直接获得的原始能力
|
||||
|
||||
微信小程序没有直接开放以下原始传感器接口:
|
||||
|
||||
- `Gravity`
|
||||
- `Linear Acceleration`
|
||||
- `Rotation Vector`
|
||||
- `Geomagnetic Field` 原始三轴
|
||||
- `Proximity`
|
||||
- 原始 `Step Counter`
|
||||
|
||||
说明:
|
||||
|
||||
- `wx.getWeRunData` 不是实时步数传感器流
|
||||
- 它更适合中长期统计,不适合实时地图玩法
|
||||
|
||||
---
|
||||
|
||||
## 3. 微信小程序推荐应用方案
|
||||
|
||||
### 3.1 第一优先级
|
||||
|
||||
#### A. Gyroscope
|
||||
|
||||
用途:
|
||||
|
||||
- 提升自动转图平滑度
|
||||
- 降低跑步中手机晃动导致的朝向抖动
|
||||
- 增强指北针和地图旋转过渡体验
|
||||
|
||||
推荐产出:
|
||||
|
||||
- `turnRate`
|
||||
- `headingSmoothFactor`
|
||||
- `headingStability`
|
||||
|
||||
#### B. DeviceMotion
|
||||
|
||||
用途:
|
||||
|
||||
- 识别手机姿态
|
||||
- 判断设备是竖持、倾斜还是接近平放
|
||||
- 配合 gyro 增强朝向可信度
|
||||
|
||||
推荐产出:
|
||||
|
||||
- `devicePose`
|
||||
- `orientationConfidence`
|
||||
- `tiltState`
|
||||
|
||||
#### C. Compass
|
||||
|
||||
用途:
|
||||
|
||||
- 静止或低速时,作为持机朝向基准
|
||||
- 指北针展示
|
||||
|
||||
推荐角色:
|
||||
|
||||
- 继续保留
|
||||
- 作为“静止朝向输入”
|
||||
- 不再单独承担跑动中的全部朝向逻辑
|
||||
|
||||
### 3.2 第二优先级
|
||||
|
||||
#### D. Accelerometer
|
||||
|
||||
用途:
|
||||
|
||||
- 辅助识别是否真的在移动
|
||||
- 识别急停、抖动、运动强度变化
|
||||
|
||||
推荐产出:
|
||||
|
||||
- `motionIntensity`
|
||||
- `movementConfidence`
|
||||
|
||||
说明:
|
||||
|
||||
- 不建议直接用原始加速度驱动地图行为
|
||||
- 应和 GPS、gyro 一起融合使用
|
||||
|
||||
#### E. Location
|
||||
|
||||
用途:
|
||||
|
||||
- 当前定位
|
||||
- 轨迹
|
||||
- 目标距离
|
||||
- movement heading
|
||||
- 速度估计
|
||||
|
||||
推荐角色:
|
||||
|
||||
- 继续作为地图和玩法核心输入
|
||||
- 后续更多与 gyro / accelerometer 配合使用
|
||||
|
||||
### 3.3 当前不建议优先投入
|
||||
|
||||
#### F. WeRunData
|
||||
|
||||
用途:
|
||||
|
||||
- 日级步数统计
|
||||
- 长周期运动数据
|
||||
|
||||
当前不建议投入原因:
|
||||
|
||||
- 不是实时传感器
|
||||
- 不适合当前地图实时玩法主链
|
||||
|
||||
---
|
||||
|
||||
## 4. 微信小程序推荐先产出的高级状态
|
||||
|
||||
### A. movementState
|
||||
|
||||
建议值:
|
||||
|
||||
- `idle`
|
||||
- `walk`
|
||||
- `run`
|
||||
|
||||
来源:
|
||||
|
||||
- GPS 速度
|
||||
- accelerometer
|
||||
- device motion
|
||||
|
||||
### B. headingSource
|
||||
|
||||
建议值:
|
||||
|
||||
- `sensor`
|
||||
- `blended`
|
||||
- `movement`
|
||||
|
||||
来源:
|
||||
|
||||
- compass
|
||||
- gyroscope
|
||||
- GPS track
|
||||
|
||||
### C. devicePose
|
||||
|
||||
建议值:
|
||||
|
||||
- `upright`
|
||||
- `tilted`
|
||||
- `flat`
|
||||
|
||||
来源:
|
||||
|
||||
- device motion
|
||||
- gyroscope
|
||||
|
||||
### D. headingConfidence
|
||||
|
||||
建议值:
|
||||
|
||||
- `low`
|
||||
- `medium`
|
||||
- `high`
|
||||
|
||||
来源:
|
||||
|
||||
- compass
|
||||
- gyroscope
|
||||
- GPS 精度
|
||||
- movement heading 是否可靠
|
||||
|
||||
---
|
||||
|
||||
## 5. 原生 Flutter App 可用传感器
|
||||
|
||||
原生 Flutter App 的能力边界明显更强,后续如果迁移或并行开发,可直接利用系统原始传感器。
|
||||
|
||||
### 5.1 可考虑直接接入
|
||||
|
||||
- `Location / GNSS`
|
||||
- `Compass / Magnetometer`
|
||||
- `Gyroscope`
|
||||
- `Accelerometer`
|
||||
- `Linear Acceleration`
|
||||
- `Gravity`
|
||||
- `Rotation Vector`
|
||||
- `Step Counter / Pedometer`
|
||||
- `Barometer`(如设备支持)
|
||||
- `Proximity`(视玩法需求)
|
||||
|
||||
说明:
|
||||
|
||||
- Flutter 本身一般通过插件获取这些能力
|
||||
- 具体以 Android / iOS 可用性差异为准
|
||||
|
||||
### 5.2 Flutter 相对小程序的主要优势
|
||||
|
||||
- 能直接拿到更完整的原始传感器矩阵
|
||||
- 更适合做高质量姿态融合
|
||||
- 更适合做步数、步频、跑动状态识别
|
||||
- 可更深度控制后台行为和采样频率
|
||||
|
||||
---
|
||||
|
||||
## 6. Flutter 推荐应用方案
|
||||
|
||||
### 6.1 第一优先级
|
||||
|
||||
#### A. Rotation Vector
|
||||
|
||||
用途:
|
||||
|
||||
- 作为地图自动转图的高质量姿态输入
|
||||
- 优于单纯磁力计 + 罗盘
|
||||
|
||||
推荐产出:
|
||||
|
||||
- `deviceHeadingDeg`
|
||||
- `devicePose`
|
||||
- `headingConfidence`
|
||||
|
||||
#### B. Gyroscope
|
||||
|
||||
用途:
|
||||
|
||||
- 旋转平滑
|
||||
- 快速转身检测
|
||||
- 姿态短时补偿
|
||||
|
||||
#### C. Linear Acceleration
|
||||
|
||||
用途:
|
||||
|
||||
- 识别运动状态
|
||||
- 急停、冲刺、抖动判定
|
||||
|
||||
推荐产出:
|
||||
|
||||
- `motionIntensity`
|
||||
- `movementState`
|
||||
|
||||
#### D. Step Counter
|
||||
|
||||
用途:
|
||||
|
||||
- 实时步数
|
||||
- 步频
|
||||
- 跑步状态识别
|
||||
- 训练/卡路里模型增强
|
||||
|
||||
推荐产出:
|
||||
|
||||
- `stepCount`
|
||||
- `cadenceSpm`
|
||||
- `movementState`
|
||||
|
||||
### 6.2 第二优先级
|
||||
|
||||
#### E. Gravity
|
||||
|
||||
用途:
|
||||
|
||||
- 持机姿态识别
|
||||
- 平放/竖持策略切换
|
||||
|
||||
#### F. Magnetometer
|
||||
|
||||
用途:
|
||||
|
||||
- 作为姿态融合底层输入
|
||||
|
||||
建议:
|
||||
|
||||
- 不建议单独直接映射到业务逻辑
|
||||
- 主要与 rotation vector / gyro 融合
|
||||
|
||||
#### G. Barometer
|
||||
|
||||
用途:
|
||||
|
||||
- 海拔变化
|
||||
- 爬升检测
|
||||
|
||||
适合:
|
||||
|
||||
- 户外定向训练
|
||||
- 赛后统计
|
||||
|
||||
---
|
||||
|
||||
## 7. Flutter 推荐先产出的高级状态
|
||||
|
||||
### A. movementState
|
||||
|
||||
建议值:
|
||||
|
||||
- `idle`
|
||||
- `walk`
|
||||
- `run`
|
||||
- `sprint`
|
||||
|
||||
来源:
|
||||
|
||||
- GPS
|
||||
- step counter
|
||||
- linear acceleration
|
||||
|
||||
### B. cadenceSpm
|
||||
|
||||
用途:
|
||||
|
||||
- 训练分析
|
||||
- 卡路里估算增强
|
||||
- 玩法资源逻辑
|
||||
|
||||
### C. devicePose
|
||||
|
||||
建议值:
|
||||
|
||||
- `upright`
|
||||
- `tilted`
|
||||
- `flat`
|
||||
|
||||
### D. headingSource
|
||||
|
||||
建议值:
|
||||
|
||||
- `sensor`
|
||||
- `blended`
|
||||
- `movement`
|
||||
|
||||
### E. headingConfidence
|
||||
|
||||
建议值:
|
||||
|
||||
- `low`
|
||||
- `medium`
|
||||
- `high`
|
||||
|
||||
### F. elevationTrend
|
||||
|
||||
建议值:
|
||||
|
||||
- `flat`
|
||||
- `ascending`
|
||||
- `descending`
|
||||
|
||||
来源:
|
||||
|
||||
- barometer
|
||||
- GPS altitude
|
||||
|
||||
---
|
||||
|
||||
## 8. 两端统一抽象建议
|
||||
|
||||
尽管两端可用传感器不同,但建议统一抽象,不让上层感知平台差异。
|
||||
|
||||
### 8.1 原始层
|
||||
|
||||
放在:
|
||||
|
||||
- `engine/sensor`
|
||||
|
||||
职责:
|
||||
|
||||
- 读取平台原始传感器
|
||||
- 做最基础的节流、归一化、权限处理
|
||||
|
||||
### 8.2 融合层
|
||||
|
||||
放在:
|
||||
|
||||
- `telemetry`
|
||||
|
||||
职责:
|
||||
|
||||
- 生成统一高级状态
|
||||
- 对外屏蔽平台差异
|
||||
|
||||
建议统一输出:
|
||||
|
||||
- `movementState`
|
||||
- `devicePose`
|
||||
- `headingSource`
|
||||
- `headingConfidence`
|
||||
- `cadenceSpm`
|
||||
- `motionIntensity`
|
||||
|
||||
### 8.3 消费层
|
||||
|
||||
#### 地图引擎消费
|
||||
|
||||
- `headingSource`
|
||||
- `devicePose`
|
||||
- `headingConfidence`
|
||||
|
||||
#### 规则层消费
|
||||
|
||||
- `movementState`
|
||||
- `cadenceSpm`
|
||||
- `motionIntensity`
|
||||
|
||||
#### HUD / Feedback 消费
|
||||
|
||||
- `movementState`
|
||||
- `cadenceSpm`
|
||||
- 心率 / 卡路里 / 训练强度
|
||||
|
||||
---
|
||||
|
||||
## 9. 推荐接入顺序
|
||||
|
||||
### 微信小程序第一阶段
|
||||
|
||||
先接:
|
||||
|
||||
- `Gyroscope`
|
||||
- `DeviceMotion`
|
||||
|
||||
目标:
|
||||
|
||||
- 提升自动转图质量
|
||||
- 产出更稳定的姿态与朝向可信度
|
||||
|
||||
### 微信小程序第二阶段
|
||||
|
||||
再接:
|
||||
|
||||
- `Accelerometer`
|
||||
|
||||
目标:
|
||||
|
||||
- 提升 movement state 识别
|
||||
|
||||
### Flutter 第一阶段
|
||||
|
||||
先接:
|
||||
|
||||
- `Rotation Vector`
|
||||
- `Gyroscope`
|
||||
- `Linear Acceleration`
|
||||
|
||||
目标:
|
||||
|
||||
- 直接建立高质量朝向与运动状态底座
|
||||
|
||||
### Flutter 第二阶段
|
||||
|
||||
再接:
|
||||
|
||||
- `Step Counter`
|
||||
- `Gravity`
|
||||
|
||||
目标:
|
||||
|
||||
- 增强运动统计与姿态判断
|
||||
|
||||
---
|
||||
|
||||
## 10. 当前最值得优先投入的方向
|
||||
|
||||
如果只从当前项目收益看,最值得优先做的是:
|
||||
|
||||
### 微信小程序
|
||||
|
||||
- `Gyroscope`
|
||||
- `DeviceMotion`
|
||||
|
||||
### Flutter
|
||||
|
||||
- `Rotation Vector`
|
||||
- `Gyroscope`
|
||||
- `Linear Acceleration`
|
||||
|
||||
原因:
|
||||
|
||||
- 这些能力最直接影响地图体验
|
||||
- 最贴近当前自动转图、前进方向、姿态识别需求
|
||||
- 复用价值高
|
||||
|
||||
---
|
||||
|
||||
## 11. 一句话结论
|
||||
|
||||
### 微信小程序
|
||||
|
||||
可用传感器有限,但足够继续做:
|
||||
|
||||
- 更稳的自动转图
|
||||
- 更好的朝向平滑
|
||||
- 更好的运动状态识别
|
||||
|
||||
最值得优先接入的是:
|
||||
|
||||
- `Gyroscope`
|
||||
- `DeviceMotion`
|
||||
- `Accelerometer`
|
||||
|
||||
### 原生 Flutter App
|
||||
|
||||
可利用的原始传感器更完整,建议未来重点发挥:
|
||||
|
||||
- `Rotation Vector`
|
||||
- `Gyroscope`
|
||||
- `Linear Acceleration`
|
||||
- `Step Counter`
|
||||
|
||||
两端都应遵守同一个原则:
|
||||
|
||||
**原始传感器进 `engine/sensor`,高级状态进 `telemetry`,上层只消费统一状态。**
|
||||
|
||||
331
doc/archive/notes/多人模拟器待办.md
Normal file
331
doc/archive/notes/多人模拟器待办.md
Normal file
@@ -0,0 +1,331 @@
|
||||
# 多人模拟器改造待开发文档
|
||||
|
||||
本文档用于记录“公网模拟器支持多人开发/多人联调”的待开发方案。
|
||||
当前仅作为设计与排期参考,不代表已经进入实现阶段。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标
|
||||
|
||||
当前外部模拟器已经支持:
|
||||
|
||||
- mock GPS
|
||||
- mock heart rate
|
||||
- 公网 WebSocket 接入
|
||||
|
||||
但当前模型更接近“单会话广播”。
|
||||
如果多人同时开发或联调,容易出现:
|
||||
|
||||
- A 的 GPS 影响 B 的小程序
|
||||
- C 的心率影响 D 的 HUD
|
||||
- 同一公网模拟器服务缺乏隔离能力
|
||||
|
||||
因此需要把模拟器体系升级成:
|
||||
|
||||
- 多房间
|
||||
- 多身份
|
||||
- 按目标订阅
|
||||
|
||||
最终目标是:
|
||||
|
||||
- 多人共用同一个公网模拟服务
|
||||
- 各自的数据流互不干扰
|
||||
- 为未来多人玩法联调留好底座
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前问题本质
|
||||
|
||||
当前模拟器通信模型更像:
|
||||
|
||||
- 一个 WebSocket 服务
|
||||
- 模拟器侧发布消息
|
||||
- 小程序侧直接接收
|
||||
|
||||
这个模型在单人开发时足够。
|
||||
但在多人开发时,缺少以下维度:
|
||||
|
||||
- `room`
|
||||
- `actorId`
|
||||
- `channel`
|
||||
|
||||
没有这些维度时,服务端无法做消息隔离与路由控制。
|
||||
|
||||
---
|
||||
|
||||
## 3. 建议的第一阶段方案
|
||||
|
||||
第一阶段不追求复杂功能,只解决“多人不串流”的核心问题。
|
||||
|
||||
### 3.1 核心模型
|
||||
|
||||
为所有模拟消息增加 3 个维度:
|
||||
|
||||
- `room`
|
||||
- `actorId`
|
||||
- `channel`
|
||||
|
||||
含义如下:
|
||||
|
||||
- `room`
|
||||
表示一个独立测试空间
|
||||
- `actorId`
|
||||
表示房间中的一个具体模拟源
|
||||
- `channel`
|
||||
表示消息类型,例如 `gps`、`heart_rate`
|
||||
|
||||
### 3.2 第一阶段目标
|
||||
|
||||
第一阶段完成后应满足:
|
||||
|
||||
- A 和 B 可以共用同一个公网模拟器服务
|
||||
- A 的小程序只接 A 的数据
|
||||
- B 的小程序只接 B 的数据
|
||||
- GPS 与心率都能隔离
|
||||
|
||||
---
|
||||
|
||||
## 4. 推荐协议
|
||||
|
||||
### 4.1 模拟器注册
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "register_simulator",
|
||||
"room": "team-dev",
|
||||
"actorId": "sim-a"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 小程序订阅
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "subscribe",
|
||||
"room": "team-dev",
|
||||
"actorId": "sim-a",
|
||||
"channels": ["gps", "heart_rate"]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 发布 GPS
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "publish",
|
||||
"room": "team-dev",
|
||||
"actorId": "sim-a",
|
||||
"channel": "gps",
|
||||
"payload": {
|
||||
"type": "mock_gps",
|
||||
"timestamp": 1711267200000,
|
||||
"lat": 31.2304,
|
||||
"lon": 121.4737,
|
||||
"accuracyMeters": 6,
|
||||
"speedMps": 2.4,
|
||||
"headingDeg": 135
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 发布心率
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "publish",
|
||||
"room": "team-dev",
|
||||
"actorId": "sim-a",
|
||||
"channel": "heart_rate",
|
||||
"payload": {
|
||||
"type": "mock_heart_rate",
|
||||
"timestamp": 1711267200000,
|
||||
"bpm": 148
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 服务端改造建议
|
||||
|
||||
### 5.1 服务端职责
|
||||
|
||||
服务端从“直接广播”升级成“按订阅路由”。
|
||||
|
||||
它需要维护每个 WebSocket 连接的元数据:
|
||||
|
||||
```ts
|
||||
type ClientSession = {
|
||||
socketId: string
|
||||
role: 'simulator' | 'app'
|
||||
room: string | null
|
||||
actorId: string | null
|
||||
channels: Set<string>
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 路由规则
|
||||
|
||||
服务端收到 `publish` 后,只转发给满足以下条件的客户端:
|
||||
|
||||
- `role === 'app'`
|
||||
- `room` 一致
|
||||
- `actorId` 一致
|
||||
- `channels` 包含当前 `channel`
|
||||
|
||||
这一步完成后,多人使用同一个公网服务时就不会互串。
|
||||
|
||||
### 5.3 第一阶段不需要的复杂能力
|
||||
|
||||
第一阶段不建议先做:
|
||||
|
||||
- 房间成员列表
|
||||
- 在线人数统计
|
||||
- 历史消息回放
|
||||
- 房间消息缓存
|
||||
- 权限控制
|
||||
|
||||
这些可以等基础隔离跑通后再扩。
|
||||
|
||||
---
|
||||
|
||||
## 6. 小程序侧改造建议
|
||||
|
||||
### 6.1 调试面板新增字段
|
||||
|
||||
建议在调试面板中新增:
|
||||
|
||||
- `Mock Room`
|
||||
- `Mock Actor`
|
||||
- `保存房间/身份`
|
||||
|
||||
当前 GPS 和心率已经都有 mock bridge,后续建议最终共用同一个逻辑目标:
|
||||
|
||||
- 同一个桥接地址
|
||||
- 同一个 `room`
|
||||
- 同一个 `actorId`
|
||||
|
||||
### 6.2 连接流程
|
||||
|
||||
小程序连上 mock bridge 后,自动发送:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "subscribe",
|
||||
"room": "...",
|
||||
"actorId": "...",
|
||||
"channels": ["gps", "heart_rate"]
|
||||
}
|
||||
```
|
||||
|
||||
这样:
|
||||
|
||||
- GPS 模拟只接自己的 `gps`
|
||||
- 心率模拟只接自己的 `heart_rate`
|
||||
|
||||
### 6.3 当前架构适配性
|
||||
|
||||
这项改造与当前架构是兼容的。
|
||||
|
||||
原因:
|
||||
|
||||
- 它主要发生在传感层和调试链
|
||||
- 不需要改规则层
|
||||
- 不需要改 telemetry 语义
|
||||
- 不需要改地图引擎主逻辑
|
||||
|
||||
---
|
||||
|
||||
## 7. 外部模拟器改造建议
|
||||
|
||||
### 7.1 第一阶段 UI 最小改动
|
||||
|
||||
模拟器左侧面板新增两个输入项:
|
||||
|
||||
- `Room`
|
||||
- `Actor ID`
|
||||
|
||||
后续所有 GPS / 心率发送都自动带上它们。
|
||||
|
||||
### 7.2 推荐默认使用方式
|
||||
|
||||
多人开发时建议:
|
||||
|
||||
- 大家共用同一个公网服务地址
|
||||
- `room` 用项目或阶段名
|
||||
- `actorId` 用开发者自己名字或实例名
|
||||
|
||||
示例:
|
||||
|
||||
- room: `team-dev`
|
||||
- actorId: `zhangsan`
|
||||
- actorId: `lisi`
|
||||
|
||||
### 7.3 后续可扩展能力
|
||||
|
||||
后续如果要继续增强,可以加:
|
||||
|
||||
- 房间成员列表
|
||||
- 一键复制当前房间配置
|
||||
- 旁观模式
|
||||
- 同房间多个 actor 同时显示
|
||||
- 共享路径模板
|
||||
|
||||
---
|
||||
|
||||
## 8. 为什么这项改造值得做
|
||||
|
||||
这不只是为了多人开发方便。
|
||||
|
||||
它还会直接为未来这些方向打基础:
|
||||
|
||||
- 多人玩法联调
|
||||
- 团队对抗玩法
|
||||
- 领地争夺玩法
|
||||
- 多角色追逐玩法
|
||||
|
||||
也就是说:
|
||||
|
||||
今天为“多人模拟器”加的 `room + actorId + channel`,未来可以直接演进成多人玩法调试底座。
|
||||
|
||||
---
|
||||
|
||||
## 9. 建议实施顺序
|
||||
|
||||
### 第一阶段
|
||||
|
||||
- 服务端支持 `register_simulator / subscribe / publish`
|
||||
- 消息带 `room + actorId + channel`
|
||||
- 小程序支持订阅指定 `room + actorId`
|
||||
- 外部模拟器增加 `room / actorId`
|
||||
|
||||
### 第二阶段
|
||||
|
||||
- 增加房间成员列表
|
||||
- 增加在线状态
|
||||
- 增加多 actor 可视化
|
||||
|
||||
### 第三阶段
|
||||
|
||||
- 接多人玩法联调
|
||||
- 接角色维度
|
||||
- 接会话回放与共享调试
|
||||
|
||||
---
|
||||
|
||||
## 10. 第一阶段验收标准
|
||||
|
||||
第一阶段完成后,至少应满足:
|
||||
|
||||
1. 两个人同时连同一个公网模拟器服务,不串 GPS
|
||||
2. 两个人同时连同一个公网模拟器服务,不串心率
|
||||
3. 同一个房间中,不同 `actorId` 可以隔离
|
||||
4. 一个小程序实例可以只接收自己配置的目标流
|
||||
|
||||
---
|
||||
|
||||
## 11. 当前结论
|
||||
|
||||
这项改造建议先保留为待开发事项。
|
||||
当前阶段不急着实现,但应作为后续多人开发与多人玩法联调的重要底座能力。
|
||||
|
||||
5
doc/archive/notes/我的待办.md
Normal file
5
doc/archive/notes/我的待办.md
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
结果页会根据客户的要求不停的变换,用什么方案能实现这个需求,其实其他的弹出内容也都存在这个问题,样式,内容都时根据客户需求变化的,怎样一种方案设计比较好呢?
|
||||
|
||||
|
||||
|
||||
26
doc/archive/归档索引.md
Normal file
26
doc/archive/归档索引.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 文档归档索引
|
||||
|
||||
这里存放的是已经完成历史使命的阶段性方案稿、重复模板和临时记录。
|
||||
|
||||
这些文档仍然保留,目的是:
|
||||
|
||||
- 方便回看历史讨论
|
||||
- 追踪某个方案为什么被放弃
|
||||
- 避免直接删除造成信息丢失
|
||||
|
||||
但它们已经不是当前推荐的主阅读入口。
|
||||
|
||||
## 归档分类
|
||||
|
||||
- [配置归档](/D:/dev/cmr-mini/doc/archive/config)
|
||||
- [动画归档](/D:/dev/cmr-mini/doc/archive/animation)
|
||||
- [体验归档](/D:/dev/cmr-mini/doc/archive/experience)
|
||||
- [临时笔记归档](/D:/dev/cmr-mini/doc/archive/notes)
|
||||
|
||||
## 当前推荐入口
|
||||
|
||||
- 总索引:[doc/index.md](/D:/dev/cmr-mini/doc/文档索引.md)
|
||||
- 配置索引:[config-docs-index.md](/D:/dev/cmr-mini/doc/config/配置文档索引.md)
|
||||
- 动画工作流:[animation-integration-workflow.md](/D:/dev/cmr-mini/doc/animation/动画接入工作流.md)
|
||||
- 混合体验架构:[hybrid-experience-architecture.md](/D:/dev/cmr-mini/doc/experience/混合体验架构方案.md)
|
||||
|
||||
Reference in New Issue
Block a user