# 游戏配置文件设计方案（阶段讨论稿）
> 文档版本：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 组合。**



