# Top Speed 赛道创建指南

## 目录

1. 简介 2. 作为作者，该如何想象赛道形状 3. 文件夹布局、文件发现与加载规则 4. TSM 文件格式规则 5. 各部分以及它们如何关联 6. 详细键参考 6.1 Meta 部分 6.2 天气配置部分 6.3 Room 部分 6.4 Segment 部分 6.5 声源部分 6.6 值解析与标记规则 7. 房间混响模型与完整预设目录 7.1 混响参数含义 7.2 完整内置房间预设 8. 完整示例 TSM 赛道文件 9. 验证与故障排查 10. 实用创作流程

## 1. 简介

本指南记录了 Top Speed 当前使用的自定义赛道格式，内容基于当前解析器与运行时代码路径整理而成。它是一份创作参考文档，而不是宣传概览，因此核心目标是实用且准确。

一个自定义赛道文件会同时控制四个方面：道路形状与路面流向、启用的天气模型及其过渡行为、车辆穿过各赛道段时所应用的声学房间配置，以及放置在赛道上的可选自定义声源。这些系统都通过 id 相互关联，并且会在赛道被接受前完成验证。

这意味着，制作赛道不仅仅是写出合法的键。你还需要理解：哪些键会真正被运行时消费，哪些键只是元数据，极端值经过钳制后会如何变化，以及哪些引用必须事先存在。

## 2. 作为作者，该如何想象赛道形状

最准确的心智模型，是把整条赛道看作一条会循环重放的赛道段时间线。赛道段顺序，就是赛道顺序；赛道段长度，是以米为单位的真实物理距离；弯道类型则决定玩家在通过该赛道段时，车道中心会以多快的速度向左或向右偏移。

`straight` 会让横向中心偏移保持为零。左右弯道则会以不同强度产生连续偏移。由于偏移是在整段长度上累计计算的，因此在弯道类型相同的情况下，长赛道段会比短赛道段产生更大的累计中心位移。

当前运行时使用的弯道强度系数如下：

- `straight`: `0`
- `easy_left` / `easy_right`: `0.5`
- `left` / `right`: `0.6667`
- `hard_left` / `hard_right`: `1.0`
- `hairpin_left` / `hairpin_right`: `1.5`

赛道段宽度与弯道形状是分开的。如果 `width > 0`，该赛道段就会使用它自己的宽度；如果 `width <= 0`，运行时会回退到默认车道宽度。这会显著影响赛道手感，因为完全相同的弯道序列，在不同宽度配置下，可能会显得宽容，也可能会显得苛刻。

一个实用的工作流是：先只编写赛道段顺序、长度和宽度，先开起来、调整节奏；等几何形状手感正确之后，再加入天气、房间和自定义声音。

## 3. 文件夹布局、文件发现与加载规则

自定义加载既可以接受文件夹路径，也可以接受文件路径。如果传入的是文件夹，加载器会扫描该文件夹中的 `*.tsm` 文件，并按照不区分大小写的排序顺序选取文件名排在最前的那个；如果传入的是文件路径，它必须是一个位于现有文件夹中的 `.tsm` 文件。

推荐布局如下：

    Tracks/
      MyTrack/
        track.tsm
        Audio/
          ambience_loop.ogg
          birds_1.ogg

声源部分中的声音路径，是相对于 `tsm` 文件所在文件夹来解析的。它们不会相对于游戏根目录解析，也不会相对于 `Sounds` 资源根目录解析。这样可以保证每个自定义赛道都是自包含的。

自定义赛道的显示名称解析顺序如下：

1. 如果 `meta.name` 已定义且非空，则优先使用它。
2. 使用父文件夹名称。
3. 使用不带扩展名的文件名。

## 4. TSM 文件格式规则

TSM 是基于行的格式，并且只支持两种语句形式：部分头和键值赋值。

部分头格式：

    [kind]
    [kind:id]

键值格式：

    key = value

行内注释以 `#` 开始，并会在解析前被剥离。空行会被忽略。

支持的部分类型包括 `meta`、`segment`、`weather`、`room` 和 `sound`。其中 `segment`、`weather`、`room` 与 `sound` 都要求带 id（即 `[kind:id]`）；`meta` 不使用 id（即 `[meta]`）。

未知的部分类型会导致验证失败。严格部分中的未知键同样会导致验证失败，唯一例外是那些以 `meta` 或 `metadata` 开头的扩展式键。

部分顺序是灵活的。解析器会自上而下读取，并在遇到新的部分头时，将前一个部分对象封存完成。同一部分中重复同一个键是允许的；在该部分对象内部，后写的值会覆盖先前的值。

键名和部分类型在解析时会被规范化为小写，并把空格与连字符转换成下划线。id 会先去掉首尾空白，并以不区分大小写的方式匹配。id 唯一性是按部分家族（`segment`、`weather`、`room`、`sound`）分别强制执行的，因此同一家族内出现重复 id 会被视为验证错误。

## 5. 各部分以及它们如何关联

赛道至少需要一个 `segment` 部分。此外，`meta.weather` 也是必填项，并且它必须指向一个已存在的天气配置 id。如果这两者任何一个缺失，赛道都会被拒绝。

赛道段是整条赛道的骨架，它们可以引用：

- 天气配置 id
- room id 或内置房间预设名称
- 声源 id

声源还可以通过 `start_area` 和 `end_area` 来引用赛道段 id，因此赛道段 id 同时会被几何系统和音频触发逻辑使用。

房间行为有两层覆盖关系：

- room 部分中的值会覆盖房间预设的基线值
- segment 中的房间覆盖键，只会覆盖该赛道段所选中的房间

天气行为也有两层：

- 来自 `meta.weather` 的赛道级默认配置
- 可选的赛道段级天气 id，以及可选的混合时间（`weather_transition_seconds`）

## 6. 详细键参考

## 6.1 Meta 部分

本部分的头写法是 `[meta]`。

`name`

这是自定义赛道在菜单和播报中使用的显示名称。
允许的值： - 任意非空文本值。

`version`

这是可选的作者元数据，用于你自己的版本修订。
它会和赛道数据一起存储，但本身不会直接改变运行时的驾驶或音频行为。
允许的值： - 任意非空文本值。

`weather`

这是赛道必填的默认天气配置 id。
允许的值： - 任意已存在的天气部分 id。 - 例如：如果文件中包含 `[weather:clear]`，那么 `weather = clear` 就是合法的。 - 如果该 id 不存在，加载会失败。

`ambience`

这个键用于选择旧式环境循环音家族。
允许的值： - `none`
禁用旧式环境循环音播放。 - `desert`
使用沙漠环境循环音行为。 - `airport`
使用机场环境循环音行为。

别名值： - `0`: `none` - `noambience`: `none` - `1`: `desert` - `2`: `airport`

## 6.2 天气配置部分

部分头写法为 `[weather:<id>]`，例如 `[weather:clear]`。

`kind`

这个键用于选择该配置的基础预设。
当设置了这个键时，运行时会先加载该预设值，然后再应用同一个天气部分中显式写出的数值键。
允许的值： - `sunny`
晴天基线，风力中性、能见度较长。 - `rain`
雨天配置基线，更强调湿润环境音。 - `wind`
大风配置基线，具有更强的横风。 - `storm`
暴风雨配置基线，天气强度最高。

别名值： - `0`: `sunny` - `1`: `rain` - `rainy`: `rain` - `2`: `wind` - `windy`: `wind` - `3`: `storm` - `stormy`: `storm`

`longitudinal_wind_mps`

这是沿驾驶轴方向的风分量，单位是米/秒。
正值相当于迎风，会提高相对风速；负值相当于顺风。
允许的值： - 任意有限数值。

`lateral_wind_mps`

这是横风分量，单位是米/秒。
它会把侧向空气速度计入空气阻力大小。
允许的值： - 任意有限数值。

`air_density`

它控制空气动力计算中使用的空气密度，单位为 kg/m³。
允许的值： - 任意大于 `0` 的数值。

运行时说明：如果构造阶段仍然收到无效的非正值，就会回退为 `1.225`。

`drafting_factor`

这是阻力模型中直接作用于空气阻力的乘数。
`1.0` 表示中性；大于 `1.0` 会增加阻力；小于 `1.0` 会减少阻力。
允许的值： - 任意大于 `0` 的数值。

运行时说明：构造阶段的钳制会强制它不低于 `0.1`。

`temperature_c`

它以摄氏度存储天气温度，用于配置状态。
当前驾驶阻力逻辑并不直接依赖这个键，但它仍然是天气配置数据的一部分。
允许的值： - 任意有限数值。

`humidity`

它以归一化比值形式存储相对湿度。
允许的值： - 任意位于 `0` 到 `1` 之间的数值。

运行时说明：在天气配置构建时，值会被钳制到 `0..1`。

`pressure_kpa`

它以 kPa 为单位存储大气压。
允许的值： - 任意大于 `0` 的数值。

运行时说明：如果构造阶段收到无效的非正值，会回退为 `101.325`。

`visibility_m`

它以米为单位存储天气配置的能见度距离。
允许的值： - 任意大于 `0` 的数值。

运行时说明：如果构造阶段收到无效的非正值，会回退为 `20000`。

`rain_gain`

这个值设定该配置中雨声循环的天气强度增益。
允许的值： - 任意大于或等于 `0` 的数值。

运行时说明：配置内部会在钳制后存储 `0..4`；当前播放路径在真正应用到声源音量前，还会再做一次最终 `0..1` 钳制。

`wind_gain`

这个值设定该配置中风声循环的天气强度增益。
允许的值： - 任意大于或等于 `0` 的数值。

运行时说明：配置内部会在钳制后存储 `0..4`；当前播放路径在真正应用到声源音量前，还会再做一次最终 `0..1` 钳制。

`storm_gain`

这个值设定该配置中暴风声循环的天气强度增益。
允许的值： - 任意大于或等于 `0` 的数值。

运行时说明：配置内部会在钳制后存储 `0..4`；当前播放路径在真正应用到声源音量前，还会再做一次最终 `0..1` 钳制。

天气切换行为：
当某个赛道段设置了 `weather = some_id` 时，运行时会逐步混合过渡到该配置。
混合速度由赛道段键 `weather_transition_seconds` 控制。

## 6.3 Room 部分

部分头写法为 `[room:<id>]`，例如 `[room:tunnel_a]`。

`name`

这是该房间配置的可选可读标签。
允许的值： - 任意非空文本值。

`room_preset`

这个键用于选择初始化房间声学时所使用的内置预设基线。
同一部分中的任何数值型房间键，都会覆盖这条基线。
允许的值： - 第 7.2 节列出的任意预设 id。

`reverb_time`

它控制房间混响尾音的总衰减时长，单位为秒。
允许的值： - 任意数值。 - 运行时会将其钳制为非负。

`reverb_gain`

它控制房间混响回传的整体电平。
允许的值： - 任意数值。 - 运行时会钳制到 `0..1`。

`hf_decay_ratio`

它控制高频在混响尾音中相对低频/中频的衰减速度。
值越低，高频越快变暗；值越高，高频亮度保留得越久。
允许的值： - 任意数值。 - 运行时会钳制到 `0..1`。

`late_reverb_gain`

它控制后期尾音成分（后期反射）的增益。
允许的值： - 任意数值。 - 运行时会钳制到 `0..1`。

`diffusion`

它控制反射的密度和平滑度。
值越低，反射听起来越分散、颗粒感越重；值越高，反射会更顺滑、更稠密。
允许的值： - 任意数值。 - 运行时会钳制到 `0..1`。

`air_absorption`

它控制声音在空气中传播时损失多少高频内容。
值越低，远处声源仍会保持较亮；值越高，远处声音会更快变闷。
允许的值： - 任意数值。 - 运行时会钳制到 `0..1`。

`occlusion_scale`

它控制障碍物对直达声路径的削弱程度。
值越低，障碍物的遮挡感越弱；值越高，障碍物的遮挡感越强。
允许的值： - 任意数值。 - 运行时会钳制到 `0..1`。

`transmission_scale`

它控制声音穿过墙体或重型结构等边界的泄漏程度。
值越低，隔墙声音越弱、阻隔感越强；值越高，穿透过去的声音越多。
允许的值： - 任意数值。 - 运行时会钳制到 `0..1`。

`occlusion_override`

这是一个可选的直接遮挡值。
一旦设置，它会覆盖房间/配置路径中派生出来的遮挡量。
允许的值： - 任意数值。 - 设置后，运行时会钳制到 `0..1`。

`transmission_override`

它设置一个传输覆盖值，并把它同时应用到低、中、高三个频段。
当你想要一个全局传输覆盖，而不是逐频段微调时，就可以用它。
允许的值： - 任意数值。 - 设置后，运行时会钳制到 `0..1`。

`transmission_override_low`

它设置低频段传输覆盖值。
允许的值： - 任意数值。 - 设置后，运行时会钳制到 `0..1`。

`transmission_override_mid`

它设置中频段传输覆盖值。
允许的值： - 任意数值。 - 设置后，运行时会钳制到 `0..1`。

`transmission_override_high`

它设置高频段传输覆盖值。
允许的值： - 任意数值。 - 设置后，运行时会钳制到 `0..1`。

`air_absorption_override`

它设置一个空气吸收覆盖值，并同时作用于低、中、高三个频段。
允许的值： - 任意数值。 - 设置后，运行时会钳制到 `0..1`。

`air_absorption_override_low`

它设置低频段空气吸收覆盖值。
允许的值： - 任意数值。 - 设置后，运行时会钳制到 `0..1`。

`air_absorption_override_mid`

它设置中频段空气吸收覆盖值。
允许的值： - 任意数值。 - 设置后，运行时会钳制到 `0..1`。

`air_absorption_override_high`

它设置高频段空气吸收覆盖值。
允许的值： - 任意数值。 - 设置后，运行时会钳制到 `0..1`。

赛道段引用行为：
一个赛道段可以引用 room 部分 id，也可以直接引用预设 id。
如果赛道段级房间覆盖存在，那么它们会在选中的房间基线之后再应用。

## 6.4 Segment 部分

部分头写法为 `[segment:<id>]`，例如 `[segment:s12]`。

`type`

这个键用于设置该赛道段的弯道类别。
所选类别会影响道路中心在这段距离推进过程中如何偏移。
允许的值： - `straight`
无横向偏移。 - `easy_left`
轻微向左偏移。 - `left`
中等向左偏移。 - `hard_left`
强烈向左偏移。 - `hairpin_left`
非常强的向左偏移。 - `easy_right`
轻微向右偏移。 - `right`
中等向右偏移。 - `hard_right`
强烈向右偏移。 - `hairpin_right`
非常强的向右偏移。

别名值： - `0`: `straight` - `1`: `easy_left` - `2`: `left` - `3`: `hard_left` - `4`: `hairpin_left` - `5`: `easy_right` - `6`: `right` - `7`: `hard_right` - `8`: `hairpin_right`

`surface`

这个键用于设置赛道段的路面类别。
允许的值： - `asphalt`
标准铺装路面行为。 - `gravel`
松散路面行为。 - `water`
湿滑/低抓地行为。 - `sand`
柔软路面行为。 - `snow`
类雪地低抓地行为。

别名值： - `0`: `asphalt` - `1`: `gravel` - `2`: `water` - `3`: `sand` - `4`: `snow`

`noise`

这个键用于设置旧式赛道段噪音行为。
允许的值： - `none`
无旧式赛道段噪音。 - `crowd`
人群环境音行为。 - `ocean`
海洋环境音行为。 - `runway`
跑道/飞机行为。 - `clock`
时钟环境音行为。 - `jet`
喷气机事件行为。 - `thunder`
雷声事件行为。 - `pile`
堆场环境音行为。 - `construction`
施工环境音行为。 - `river`
河流环境音行为。 - `helicopter`
直升机事件行为。 - `owl`
猫头鹰事件行为。

别名值： - `0`: `none` - `nonoise`: `none` - `off`: `none` - `1`: `crowd` - `2`: `ocean` - `3`: `runway` - `4`: `clock` - `5`: `jet` - `6`: `thunder` - `7`: `pile` - `8`: `construction` - `9`: `river` - `10`: `helicopter` - `11`: `owl`

`length`

这个键用于设置赛道段长度，单位为米。
解析器会强制执行最小赛道段长度阈值。
允许的值： - 任意数值。
低于当前解析器最小值的内容会给出警告，并被钳制。
当前默认最小值是 `50` 米。

`width`

这个键用于设置该赛道段的宽度覆盖值，单位为米。
允许的值： - 任意数值。 - 运行时会将其钳制为 `>= 0`。 - 非正值会回退到默认车道宽度。

`height`

它存储赛道段高度值。
允许的值： - 任意数值。 - 会被解析并存储，但当前活动道路几何行为并不会使用它。

`weather`

这个键为该赛道段指定天气配置 id。
允许的值： - 仅限已存在的天气配置 id。

`weather_transition_seconds`

这个键设置进入该赛道段天气配置时的混合持续时间。
允许的值： - 有限且 `>= 0` 的数值。

`room`

这个键为该赛道段指定房间/声学来源。
允许的值： - 已存在的 room 部分 id。 - 内置房间预设 id。

别名键： - `room_profile`: `room` - `room_preset`: `room`

`room_profile`

这是 `room` 的别名键。
允许的值： - 与 `room` 相同。

`room_preset`

这是 `room` 的别名键。
允许的值： - 与 `room` 相同。

`sound_sources`

它将声源 id 直接分配给该赛道段。
允许的值： - 已存在声源 id 的逗号分隔列表。

别名键： - `sound_source_ids`: `sound_sources`

`sound_source_ids`

这是 `sound_sources` 的别名键。
允许的值： - 与 `sound_sources` 相同。

额外的赛道段说明：
房间数值键和房间覆盖键也可以直接出现在赛道段部分中。
当它们存在时，只会覆盖这个赛道段的房间声学效果。

简单成形策略：

1. 用 `type` + `length` 构建形状。
2. 设定稳定的 `width` 宽度带。
3. 加入 `surface`。
4. 加入天气/房间过渡。
5. 最后再加声音。

## 6.5 声源部分

部分头支持两种形式：

- `[sound:<id>]`
- `[sound_<type>:<id>]` 在这种形式下，`<type>` 可以是：
- `ambient`
- `static`
- `moving`
- `random`

例如：`[sound_random:wind_gusts]` 会隐式设置 `type = random`。

`type`

这个键用于设置声源行为类型。
允许的值： - `ambient`
环境型声源行为。 - `static`
固定声源行为。 - `moving`
路径/运动驱动型声源行为。 - `random`
变体选择型声源行为。

别名值： - `0`: `ambient` - `1`: `static` - `2`: `moving` - `3`: `random`

`path`

主声源文件路径。
允许的值： - 仅限相对于赛道的路径。

别名键： - `file`: `path`

`file`

这是 `path` 的别名键。
允许的值： - 与 `path` 相同。

`variant_paths`

用于选择的额外文件变体。
允许的值： - 逗号分隔的赛道相对文件路径列表。 - 空列表无效。

`variant_source_ids`

通过引用其他声源 id 来添加额外变体。
允许的值： - 已存在声源 id 的逗号分隔列表。 - 空列表无效。

`random_mode`

变体刷新模式。
允许的值： - `onstart`
在创建/启动时选择一次变体，之后保持不变。 - `perarea`
当区域/赛道段上下文变化时刷新变体。

别名值： - `0`: `onstart` - `1`: `perarea`

`loop`

循环播放标志。
允许的值： - `true`
启用。 - `false`
禁用。

别名值： - `1`: `true` - `0`: `false` - `yes`: `true` - `no`: `false` - `on`: `true` - `off`: `false`

`spatial`

启用空间化声源行为。
允许的值： - `true`
启用。 - `false`
禁用。

别名值： - `1`: `true` - `0`: `false` - `yes`: `true` - `no`: `false` - `on`: `true` - `off`: `false`

`allow_hrtf`

允许空间化路径中使用 HRTF。
允许的值： - `true`
启用。 - `false`
禁用。

别名值： - `1`: `true` - `0`: `false` - `yes`: `true` - `no`: `false` - `on`: `true` - `off`: `false`

`global`

让声源全局保持激活，而不受区域/赛道段门控限制。
允许的值： - `true`
启用。 - `false`
禁用。

别名值： - `1`: `true` - `0`: `false` - `yes`: `true` - `no`: `false` - `on`: `true` - `off`: `false`

`volume`

基础声源增益。
允许的值： - 任意数值。 - 运行时会钳制到 `0..1`。

`fade_in`

淡入时间，单位秒。
允许的值： - 任意数值。 - 运行时会钳制为非负。

`fade_out`

淡出时间，单位秒。
允许的值： - 任意数值。 - 运行时会钳制为非负。

`crossfade_seconds`

变体切换时的交叉淡化时间。
允许的值： - 任意数值。 - 运行时会钳制为非负。

`pitch`

播放音高系数。
允许的值： - 任意数值。 - 运行时会把 `<= 0` 的值替换成 `1`。

`pan`

立体声声像值。
允许的值： - 任意数值。 - 运行时会钳制到 `-1..1`。

`min_distance`

空间化最小距离设置。
允许的值： - 任意数值。

`max_distance`

空间化最大距离设置。
允许的值： - 任意数值。

`rolloff`

空间化距离衰减设置。
允许的值： - 任意数值。

`start_area`

激活起始用的赛道段 id 触发器。
允许的值： - 仅限已存在的赛道段 id。

`end_area`

激活结束用的赛道段 id 触发器。
允许的值： - 仅限已存在的赛道段 id。

`start_position`

激活起始位置。
允许的值： - 向量文本 `x,y,z`。

`end_position`

激活结束位置。
允许的值： - 向量文本 `x,y,z`。

`position`

静态或锚点位置。
允许的值： - 向量文本 `x,y,z`。

`start_radius`

起始位置触发半径。
允许的值： - 任意数值。

运行时说明：若缺失或为非正值，在触发判定时会回退为 `1` 米。

`end_radius`

结束位置触发半径。
允许的值： - 任意数值。

运行时说明：若缺失或为非正值，在触发判定时会回退为 `1` 米。

`speed`

移动声源速度值。
允许的值： - 任意数值。

别名键： - `speed_meters_per_second`: `speed`

`speed_meters_per_second`

这是 `speed` 的别名键。
允许的值： - 与 `speed` 相同。

声路径验证说明：
绝对路径、带盘符路径，以及路径穿越片段都会被拒绝。

运行时激活摘要：
如果 `global = true`，声源会在不进行区域/赛道段检查的情况下保持激活。
否则，激活与否取决于赛道段分配、区域跨度，以及起止触发条件。

## 6.6 值解析与标记规则

数值解析使用 invariant-culture，因此小数点必须写成 `.`。
枚举解析不区分大小写，并且容忍标记文本中出现空格、连字符和下划线。
布尔值解析接受 `1/0`、`true/false`、`yes/no` 和 `on/off`。

向量键必须严格写成三个由逗号分隔的浮点值（`x,y,z`）。
在严格部分中，未知键会被视为验证错误，除非键名以 `meta` 或 `metadata` 开头。
声路径安全规则会拒绝根路径、带盘符路径，以及路径穿越片段（即 `.` 和 `..` 这样的路径组件）。
交叉引用验证会在解析结束后执行，用来确保被引用的天气 id、房间 id、赛道段 id 和声源 id 全部存在。

## 7. 房间混响模型与完整预设目录

## 7.1 混响参数含义

本节会用通俗语言解释每一个房间参数。

参数 | 它控制什么 | 增大后会发生什么
--- | --- | ---
`reverb_time_seconds` | 混响尾音在完全衰减前会持续多久。 | 空间听起来会更长、更有残留感。
`reverb_gain` | 混响回传的整体强度。 | 相较于干声，反射声会更响。
`hf_decay_ratio` | 高频在尾音中相对其他频段能保留多久。 | 高频内容会更久地保持明亮。
`late_reverb_gain` | 后期、稠密尾音成分的电平。 | 后期混响主体会更强、更明显。
`diffusion` | 早期与后期反射的稠密度/平滑度。 | 反射会显得更不离散、更融合、更平顺。
`air_absorption` | 声音在空气中随距离造成的高频损失。 | 远处声音会损失更多亮度与清晰度。
`occlusion_scale` | 障碍物削弱直达声的强度。 | 障碍物的遮挡效果会更强。
`transmission_scale` | 声音穿过墙体/结构的程度。 | 你能听到更多穿透边界泄漏出来的声音。

## 7.2 完整内置房间预设

这张表为每个混响参数都单独使用一列，因此你可以直接对照各值，而不需要再去解读元组缩写。

类别 | 预设 | 声学特征 | reverb_time_seconds | reverb_gain | hf_decay_ratio | late_reverb_gain | diffusion | air_absorption | occlusion_scale | transmission_scale
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
户外环境 | `outdoor_open` | 非常开阔的平原，尾音极少。 | 0.35 | 0.08 | 0.85 | 0.08 | 0.20 | 0.65 | 0.35 | 0.75
户外环境 | `outdoor_field` | 开阔田野，比平原略厚一点。 | 0.45 | 0.10 | 0.82 | 0.10 | 0.25 | 0.62 | 0.38 | 0.72
户外环境 | `outdoor_urban` | 密集城市反射，回返能量更强。 | 0.90 | 0.22 | 0.70 | 0.24 | 0.45 | 0.48 | 0.55 | 0.52
户外环境 | `outdoor_suburban` | 混合型郊区反射，中等尾音。 | 0.75 | 0.18 | 0.74 | 0.20 | 0.38 | 0.50 | 0.48 | 0.58
户外环境 | `outdoor_forest` | 高频衰减更重，带有树木吸收感。 | 0.70 | 0.16 | 0.52 | 0.18 | 0.32 | 0.82 | 0.60 | 0.60
户外环境 | `outdoor_mountains` | 更大的反射空间，尾音更长。 | 1.80 | 0.34 | 0.52 | 0.36 | 0.42 | 0.42 | 0.55 | 0.52
户外环境 | `outdoor_desert` | 干燥开阔地形，反射主体较克制。 | 0.55 | 0.12 | 0.76 | 0.12 | 0.26 | 0.58 | 0.42 | 0.66
户外环境 | `outdoor_snowfield` | 开阔但高频更柔和，带雪地阻尼感。 | 0.90 | 0.20 | 0.62 | 0.24 | 0.34 | 0.70 | 0.45 | 0.62
户外环境 | `outdoor_coast` | 海岸线式开阔感，中等反射。 | 0.85 | 0.20 | 0.68 | 0.22 | 0.36 | 0.52 | 0.46 | 0.60
户外环境 | `outdoor_valley` | 开阔山谷感，回返更宽广。 | 1.60 | 0.30 | 0.56 | 0.34 | 0.46 | 0.46 | 0.58 | 0.52
隧道 | `tunnel_short` | 短隧道，反射清晰。 | 1.10 | 0.48 | 0.62 | 0.52 | 0.72 | 0.22 | 0.80 | 0.32
隧道 | `tunnel_medium` | 中等长度隧道，尾音更厚。 | 1.80 | 0.60 | 0.56 | 0.62 | 0.78 | 0.20 | 0.86 | 0.26
隧道 | `tunnel_long` | 长隧道，后期声场持续更久。 | 2.70 | 0.72 | 0.50 | 0.76 | 0.82 | 0.18 | 0.90 | 0.22
隧道 | `tunnel_concrete` | 硬质混凝土隧道特征。 | 2.10 | 0.66 | 0.54 | 0.70 | 0.80 | 0.20 | 0.88 | 0.24
隧道 | `tunnel_brick` | 砖砌隧道，音色更暖、尾音略短。 | 1.70 | 0.58 | 0.58 | 0.62 | 0.76 | 0.22 | 0.84 | 0.30
隧道 | `tunnel_metal` | 金属隧道，回返能量更明亮。 | 2.00 | 0.70 | 0.46 | 0.74 | 0.84 | 0.16 | 0.88 | 0.22
隧道 | `tunnel_stone` | 石质隧道，后期响应沉重。 | 2.40 | 0.68 | 0.50 | 0.72 | 0.80 | 0.18 | 0.90 | 0.24
下穿与桥下结构 | `underpass_small` | 紧凑下穿，回返快而稠密。 | 0.95 | 0.38 | 0.62 | 0.42 | 0.62 | 0.26 | 0.78 | 0.34
下穿与桥下结构 | `underpass_large` | 更大的下穿，反射列车更长。 | 1.35 | 0.46 | 0.56 | 0.52 | 0.68 | 0.24 | 0.82 | 0.30
下穿与桥下结构 | `overhang` | 局部遮盖，反射扩散中等。 | 0.75 | 0.30 | 0.66 | 0.34 | 0.56 | 0.28 | 0.72 | 0.38
下穿与桥下结构 | `bridge_truss` | 桥梁桁架腔体反应。 | 0.65 | 0.24 | 0.64 | 0.26 | 0.44 | 0.34 | 0.60 | 0.46
车库与停车空间 | `garage_small` | 小型车库，反射紧凑。 | 0.95 | 0.40 | 0.64 | 0.44 | 0.62 | 0.30 | 0.72 | 0.34
车库与停车空间 | `garage_medium` | 中型车库，尾音更宽。 | 1.30 | 0.48 | 0.60 | 0.52 | 0.68 | 0.28 | 0.74 | 0.30
车库与停车空间 | `garage_large` | 大型车库，后期声场更强。 | 1.80 | 0.56 | 0.58 | 0.60 | 0.72 | 0.26 | 0.76 | 0.28
车库与停车空间 | `parking_open` | 以开放为主的停车结构。 | 0.70 | 0.20 | 0.70 | 0.22 | 0.38 | 0.40 | 0.48 | 0.58
车库与停车空间 | `parking_covered` | 有顶停车区，反射更强。 | 1.20 | 0.44 | 0.60 | 0.48 | 0.64 | 0.28 | 0.72 | 0.34
车库与停车空间 | `parking_underground` | 地下停车场，包围感强且持续。 | 1.90 | 0.62 | 0.54 | 0.66 | 0.76 | 0.22 | 0.84 | 0.24
仓库与工业大厅 | `warehouse_small` | 小仓库，房间响应清晰。 | 1.10 | 0.38 | 0.62 | 0.42 | 0.66 | 0.30 | 0.70 | 0.34
仓库与工业大厅 | `warehouse_medium` | 中型仓库，尾音更饱满。 | 1.70 | 0.50 | 0.56 | 0.56 | 0.74 | 0.28 | 0.74 | 0.30
仓库与工业大厅 | `warehouse_large` | 大型仓库，衰减更长。 | 2.40 | 0.62 | 0.50 | 0.68 | 0.80 | 0.24 | 0.78 | 0.26
仓库与工业大厅 | `factory_hall` | 工业大厅，反射稠密。 | 2.20 | 0.60 | 0.48 | 0.66 | 0.78 | 0.24 | 0.80 | 0.26
仓库与工业大厅 | `machine_shop` | 机修车间，比仓库大厅更紧。 | 1.30 | 0.44 | 0.54 | 0.50 | 0.70 | 0.26 | 0.76 | 0.30
机库与交通站点 | `hangar_small` | 小型机库，反射宽阔。 | 2.00 | 0.56 | 0.54 | 0.60 | 0.76 | 0.24 | 0.72 | 0.30
机库与交通站点 | `hangar_large` | 超大型机库，混响场很长。 | 3.10 | 0.68 | 0.48 | 0.74 | 0.82 | 0.22 | 0.76 | 0.26
机库与交通站点 | `airport_terminal` | 航站楼走廊式反射风格。 | 1.80 | 0.52 | 0.58 | 0.58 | 0.74 | 0.28 | 0.66 | 0.36
机库与交通站点 | `subway_station` | 深层地铁站，封闭尾音很强。 | 2.30 | 0.64 | 0.50 | 0.70 | 0.80 | 0.22 | 0.84 | 0.24
机库与交通站点 | `rail_station` | 大型火车站，但没有地铁站那么封闭。 | 1.90 | 0.54 | 0.56 | 0.60 | 0.74 | 0.26 | 0.72 | 0.32
走廊、地下室与封闭空间 | `corridor_short` | 短走廊，反馈迅速。 | 0.85 | 0.36 | 0.62 | 0.40 | 0.58 | 0.30 | 0.72 | 0.34
走廊、地下室与封闭空间 | `corridor_long` | 长走廊，反射被拉长。 | 1.40 | 0.50 | 0.56 | 0.56 | 0.68 | 0.26 | 0.80 | 0.28
走廊、地下室与封闭空间 | `stairwell_concrete` | 混凝土楼梯井，后期回返扎实。 | 1.60 | 0.54 | 0.54 | 0.58 | 0.70 | 0.26 | 0.78 | 0.28
走廊、地下室与封闭空间 | `basement_low` | 低矮地下室，中等封闭尾音。 | 1.10 | 0.44 | 0.58 | 0.48 | 0.66 | 0.28 | 0.76 | 0.30
走廊、地下室与封闭空间 | `basement_large` | 大型地下室，后期能量更重。 | 1.90 | 0.58 | 0.52 | 0.64 | 0.76 | 0.24 | 0.82 | 0.26
走廊、地下室与封闭空间 | `bunker` | 厚重的封闭地堡响应。 | 2.10 | 0.64 | 0.46 | 0.70 | 0.80 | 0.20 | 0.90 | 0.20
走廊、地下室与封闭空间 | `vault` | 高度封闭的金库特征。 | 2.60 | 0.72 | 0.42 | 0.78 | 0.84 | 0.18 | 0.92 | 0.18
大厅、竞技场与体育场 | `hall_small` | 小型大厅，反射连贯。 | 1.10 | 0.40 | 0.64 | 0.44 | 0.70 | 0.30 | 0.68 | 0.34
大厅、竞技场与体育场 | `hall_medium` | 中型大厅，密度更饱满。 | 1.70 | 0.52 | 0.58 | 0.56 | 0.78 | 0.28 | 0.72 | 0.30
大厅、竞技场与体育场 | `hall_large` | 大型大厅，空间尾音更长。 | 2.70 | 0.62 | 0.50 | 0.66 | 0.82 | 0.24 | 0.78 | 0.26
大厅、竞技场与体育场 | `arena_indoor` | 室内大场馆，扩散度高。 | 3.00 | 0.66 | 0.48 | 0.72 | 0.84 | 0.24 | 0.70 | 0.32
大厅、竞技场与体育场 | `stadium_open` | 开放式体育场，封闭感更轻。 | 1.50 | 0.45 | 0.60 | 0.50 | 0.70 | 0.40 | 0.40 | 0.60
大厅、竞技场与体育场 | `stadium_closed` | 封闭式体育场，后期声场延展更明显。 | 2.80 | 0.64 | 0.50 | 0.70 | 0.82 | 0.28 | 0.68 | 0.34
房间与录音室 | `room_small` | 小房间，响应短且聚焦。 | 0.70 | 0.30 | 0.70 | 0.32 | 0.62 | 0.36 | 0.60 | 0.40
房间与录音室 | `room_medium` | 中等房间，主体与尾音平衡。 | 1.10 | 0.40 | 0.62 | 0.42 | 0.70 | 0.30 | 0.62 | 0.34
房间与录音室 | `room_large` | 大房间，尾音拖得更久。 | 1.80 | 0.50 | 0.54 | 0.54 | 0.76 | 0.26 | 0.68 | 0.30
房间与录音室 | `studio_dry` | 干声录音室，尾音很短。 | 0.35 | 0.12 | 0.78 | 0.14 | 0.40 | 0.50 | 0.40 | 0.70
房间与录音室 | `studio_live` | 现场录音室风格，空间感更明显。 | 0.90 | 0.34 | 0.66 | 0.38 | 0.66 | 0.34 | 0.58 | 0.44
房间与录音室 | `broadcast_booth` | 非常干的播音间，控制极强。 | 0.28 | 0.08 | 0.82 | 0.10 | 0.30 | 0.58 | 0.45 | 0.70
教堂、洞穴、峡谷与下水道 | `church_small` | 小型教堂，尾音很明显。 | 2.40 | 0.56 | 0.46 | 0.60 | 0.82 | 0.26 | 0.72 | 0.30
教堂、洞穴、峡谷与下水道 | `church_large` | 大型教堂，混响绽放更长。 | 3.80 | 0.70 | 0.40 | 0.76 | 0.86 | 0.22 | 0.78 | 0.24
教堂、洞穴、峡谷与下水道 | `cathedral` | 非常大的神圣大厅，衰减极长。 | 5.40 | 0.78 | 0.34 | 0.84 | 0.90 | 0.20 | 0.82 | 0.20
教堂、洞穴、峡谷与下水道 | `cave_small` | 小型洞穴，封闭反射稠密。 | 2.60 | 0.62 | 0.46 | 0.66 | 0.74 | 0.20 | 0.86 | 0.24
教堂、洞穴、峡谷与下水道 | `cave_large` | 大型洞穴，尾音长而偏暗。 | 4.50 | 0.78 | 0.34 | 0.84 | 0.84 | 0.16 | 0.92 | 0.16
教堂、洞穴、峡谷与下水道 | `cave_ice` | 冰洞风格，回返明亮且拖尾更久。 | 3.80 | 0.72 | 0.40 | 0.80 | 0.80 | 0.24 | 0.86 | 0.22
教堂、洞穴、峡谷与下水道 | `canyon_narrow` | 狭窄峡谷，回返方向性明显。 | 2.10 | 0.54 | 0.46 | 0.58 | 0.52 | 0.34 | 0.64 | 0.42
教堂、洞穴、峡谷与下水道 | `canyon_wide` | 宽峡谷，扩散更广、封闭感更弱。 | 2.90 | 0.60 | 0.44 | 0.64 | 0.48 | 0.36 | 0.56 | 0.48
教堂、洞穴、峡谷与下水道 | `sewer_brick` | 砖砌下水道，潮湿而封闭的尾音。 | 2.10 | 0.62 | 0.50 | 0.68 | 0.78 | 0.22 | 0.86 | 0.22
教堂、洞穴、峡谷与下水道 | `sewer_concrete` | 混凝土下水道，反射硬朗。 | 2.40 | 0.68 | 0.46 | 0.74 | 0.82 | 0.20 | 0.88 | 0.20

## 8. 完整示例 TSM 赛道文件

    [meta]
    name = Demo Accessible Track
    version = 1
    weather = clear
    ambience = noambience

    [weather:clear]
    kind = sunny
    longitudinal_wind_mps = 0
    lateral_wind_mps = 0
    air_density = 1.225
    drafting_factor = 1
    temperature_c = 22
    humidity = 0.45
    pressure_kpa = 101.325
    visibility_m = 20000
    rain_gain = 0
    wind_gain = 0
    storm_gain = 0

    [weather:storm_edge]
    kind = storm
    longitudinal_wind_mps = 4
    lateral_wind_mps = 8
    air_density = 1.24
    drafting_factor = 1
    temperature_c = 17
    humidity = 1
    pressure_kpa = 99.8
    visibility_m = 3500
    rain_gain = 0.2
    wind_gain = 0.4
    storm_gain = 1.0

    [room:open_day]
    room_preset = outdoor_open

    [room:tunnel_a]
    room_preset = tunnel_medium
    reverb_gain = 0.62
    late_reverb_gain = 0.65

    [sound_ambient:birds]
    path = Audio/birds_loop.ogg
    loop = true
    volume = 0.5
    spatial = false

    [sound_random:wind_gusts]
    variant_paths = Audio/wind_gust_1.ogg, Audio/wind_gust_2.ogg, Audio/wind_gust_3.ogg
    random_mode = perarea
    loop = true
    volume = 0.4
    spatial = true
    allow_hrtf = true
    fade_in = 0.15
    fade_out = 0.15
    crossfade_seconds = 0.5
    start_area = s2
    end_area = s5

    [segment:s1]
    type = straight
    surface = asphalt
    noise = none
    length = 300
    width = 10
    room = open_day
    sound_sources = birds

    [segment:s2]
    type = easy_right
    surface = asphalt
    noise = crowd
    length = 220
    room = open_day
    sound_source_ids = wind_gusts

    [segment:s3]
    type = hard_right
    surface = asphalt
    noise = none
    length = 180
    room = tunnel_a
    weather = storm_edge
    weather_transition_seconds = 2.5

    [segment:s4]
    type = straight
    surface = asphalt
    noise = jet
    length = 260
    room = tunnel_a

    [segment:s5]
    type = left
    surface = asphalt
    noise = none
    length = 240
    room = open_day
    weather = clear
    weather_transition_seconds = 1.5

## 9. 验证与故障排查

在构建运行时对象之前，赛道加载会先经过一个严格的验证阶段。错误会直接阻止加载；警告则允许继续加载，但某些值可能会被钳制规则调整。

常见的硬失败以及直接修复方法：

失败项 | 为什么会发生 | 修复方法
--- | --- | ---
缺少默认天气配置 | `meta.weather` 为空，或指向了未知 id | 在 `meta` 中定义 `weather = some_id`，并添加 `[weather:some_id]` 部分
部分 id 重复 | 同一家族中的两个部分复用了同一个 id | 重命名 id，确保每个 `segment`、`weather`、`room` 和 `sound` id 都唯一
未知枚举标记 | 值文本无法映射到受支持的枚举标记/整数 | 使用本指南中的合法标记，或合法的数字枚举值
严格部分中出现未知键 | 键名拼写错误，或该键根本不受支持 | 修正拼写；自定义标签请放在 `meta*` / `metadata*` 前缀下
未知引用 id | 赛道段或声音引用了不存在的 id | 添加缺失的目标部分，或修正引用 id 文本
无效的声路径 | 使用了绝对路径、带盘符路径或路径穿越 | 只使用赛道相对路径，例如 `Audio/wind.ogg`
无效向量 | 位置值不是严格的 `x,y,z` 浮点格式 | 严格写成由逗号分隔的三个浮点值

需要特别注意的非致命行为：

- 小于 50 的赛道段长度会给出警告，并被钳制到 50。
- 许多房间和天气增益字段会在运行时构造器中被钳制。
- 对于声源，如果 `pitch <= 0`，会变成 `1.0`。
- `width <= 0` 不会导致失败；运行时会回退到默认车道宽度。

## 10. 实用创作流程

一个稳定的工作流，是把制作分成四轮，并且每轮都测试，而不是一口气把所有系统一次性写完。

第一轮只做几何：`meta`、一个天气配置，以及包含 `type`、`length`、`surface`、`width` 的赛道段。先确认节奏、弯道流动感和圈速手感。

第二轮做天气过渡：加入额外的天气配置，然后只在你确实想要发生天气切换的地方，为赛道段添加 `weather` 和 `weather_transition_seconds`。确认过渡是否平滑，并且是否发生在预期的赛道段边界上。

第三轮做声学：为隧道、封闭结构等特定区域加入房间预设或房间部分。如果需要局部微调，请优先使用赛道段级房间覆盖，而不是复制大量房间部分。

第四轮做声源：先从一个路径已知正确的简单声源开始，然后再逐步加入空间化行为、区域触发和随机变体。验证来自赛道段的每一个声源 id 引用，以及来自声触发器的每一个区域 id 引用。

最好再另外保留一份纯文本顺序说明，用来描述赛道段顺序、距离和设计意图。当行为不对时，把这份顺序说明与运行时表现一对照，就能更容易判断问题是出在几何、天气 id、房间选择，还是声触发引用上。