Files
cmr-mini/doc/archive/config/配置设计方案.md

594 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 游戏配置文件设计方案(阶段讨论稿)
> 文档版本v1.0
> 最后更新2026-04-02 08:28:05
本文档用于整理当前阶段推荐的配置文件设计方案,供后端、客户端和后台管理设计参考。
目标是让配置真正成为游戏的驱动入口,同时兼顾后续多玩法、多资源、多活动复用。
---
## 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 组合。**