# 游戏配置文件设计方案(阶段讨论稿) 本文档用于整理当前阶段推荐的配置文件设计方案,供后端、客户端和后台管理设计参考。 目标是让配置真正成为游戏的驱动入口,同时兼顾后续多玩法、多资源、多活动复用。 --- ## 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 组合。**