333 lines
12 KiB
Markdown
333 lines
12 KiB
Markdown
|
||
|
||
## 《极限竞速:地平线》系列 UDP 数据包完整定义
|
||
|
||
### 基础信息
|
||
|
||
| 属性 | 值 |
|
||
|------|-----|
|
||
| **协议** | UDP(单向发送,游戏不接收数据) |
|
||
| **包大小** | **324 字节(FH6)/ 约 311 字节(FH5)** |
|
||
| **发送频率** | 等于游戏帧率 |
|
||
| **字节序** | **Little Endian**(小端序) |
|
||
| **数据起始** | 玩家开始驾驶后发送,菜单/暂停/回放时不发送 |
|
||
|
||
> FH6 和 FH5 的数据包基本兼容,主要差异是 FH6 新增了 `CarGroup`、`SmashableVelDiff`、`SmashableMass` 三个字段。
|
||
|
||
---
|
||
|
||
### 类型符号说明
|
||
|
||
| 符号 | 含义 | 示例 |
|
||
|------|------|------|
|
||
| `S8` / `S32` | 有符号整数(signed) | S8 范围 -128~127 |
|
||
| `U8` / `U16` / `U32` | 无符号整数(unsigned) | U8 范围 0~255 |
|
||
| `F32` | 32位浮点数(float) | 小端序 |
|
||
|
||
---
|
||
|
||
### 完整数据包结构(按顺序)
|
||
|
||
#### 第 1 部分:状态和时间(8 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 0 | 4 | S32 | `IsRaceOn` | 1=比赛中,0=菜单/停止 |
|
||
| 4 | 4 | U32 | `TimestampMS` | 时间戳(毫秒,会溢出归零) |
|
||
|
||
#### 第 2 部分:引擎数据(12 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 8 | 4 | F32 | `EngineMaxRpm` | 引擎最大转速 |
|
||
| 12 | 4 | F32 | `EngineIdleRpm` | 引擎怠速转速 |
|
||
| 16 | 4 | F32 | `CurrentEngineRpm` | 当前引擎转速 |
|
||
|
||
#### 第 3 部分:加速度与速度(24 字节,车身坐标系 X=右, Y=上, Z=前)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 |
|
||
|------|------|------|------|
|
||
| 20 | 4 | F32 | `AccelerationX` |
|
||
| 24 | 4 | F32 | `AccelerationY` |
|
||
| 28 | 4 | F32 | `AccelerationZ` |
|
||
| 32 | 4 | F32 | `VelocityX` |
|
||
| 36 | 4 | F32 | `VelocityY` |
|
||
| 40 | 4 | F32 | `VelocityZ` |
|
||
|
||
#### 第 4 部分:角速度与方向(24 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 44 | 4 | F32 | `AngularVelocityX` | 俯仰(rad/s) |
|
||
| 48 | 4 | F32 | `AngularVelocityY` | 偏航(rad/s) |
|
||
| 52 | 4 | F32 | `AngularVelocityZ` | 侧倾(rad/s) |
|
||
| 56 | 4 | F32 | `Yaw` | 偏航角(弧度) |
|
||
| 60 | 4 | F32 | `Pitch` | 俯仰角(弧度) |
|
||
| 64 | 4 | F32 | `Roll` | 侧倾角(弧度) |
|
||
|
||
#### 第 5 部分:悬挂行程(16 字节,归一化 0=最大拉伸, 1=最大压缩)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 |
|
||
|------|------|------|------|
|
||
| 68 | 4 | F32 | `NormalizedSuspensionTravelFrontLeft` |
|
||
| 72 | 4 | F32 | `NormalizedSuspensionTravelFrontRight` |
|
||
| 76 | 4 | F32 | `NormalizedSuspensionTravelRearLeft` |
|
||
| 80 | 4 | F32 | `NormalizedSuspensionTravelRearRight` |
|
||
|
||
#### 第 6 部分:轮胎滑移比(16 字节,0=100%抓地力,|ratio|>1=失去抓地力)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 |
|
||
|------|------|------|------|
|
||
| 84 | 4 | F32 | `TireSlipRatioFrontLeft` |
|
||
| 88 | 4 | F32 | `TireSlipRatioFrontRight` |
|
||
| 92 | 4 | F32 | `TireSlipRatioRearLeft` |
|
||
| 96 | 4 | F32 | `TireSlipRatioRearRight` |
|
||
|
||
#### 第 7 部分:车轮转速(16 字节,弧度/秒)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 |
|
||
|------|------|------|------|
|
||
| 100 | 4 | F32 | `WheelRotationSpeedFrontLeft` |
|
||
| 104 | 4 | F32 | `WheelRotationSpeedFrontRight` |
|
||
| 108 | 4 | F32 | `WheelRotationSpeedRearLeft` |
|
||
| 112 | 4 | F32 | `WheelRotationSpeedRearRight` |
|
||
|
||
#### 第 8 部分:车轮状态(16 字节,0/1布尔值)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 |
|
||
|------|------|------|------|
|
||
| 116 | 4 | S32 | `WheelOnRumbleStripFrontLeft` |
|
||
| 120 | 4 | S32 | `WheelOnRumbleStripFrontRight` |
|
||
| 124 | 4 | S32 | `WheelOnRumbleStripRearLeft` |
|
||
| 128 | 4 | S32 | `WheelOnRumbleStripRearRight` |
|
||
|
||
#### 第 9 部分:涉水深度(16 字节,FH5 为深度值,FH6 为 0/1)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 |
|
||
|------|------|------|------|
|
||
| 132 | 4 | S32/F32 | `WheelInPuddleFrontLeft` |
|
||
| 136 | 4 | S32/F32 | `WheelInPuddleFrontRight` |
|
||
| 140 | 4 | S32/F32 | `WheelInPuddleRearLeft` |
|
||
| 144 | 4 | S32/F32 | `WheelInPuddleRearRight` |
|
||
|
||
> ⚠️ FH5 为 F32(水深值),FH6 为 S32(0/1布尔值)
|
||
|
||
#### 第 10 部分:路面震动反馈(16 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 |
|
||
|------|------|------|------|
|
||
| 148 | 4 | F32 | `SurfaceRumbleFrontLeft` |
|
||
| 152 | 4 | F32 | `SurfaceRumbleFrontRight` |
|
||
| 156 | 4 | F32 | `SurfaceRumbleRearLeft` |
|
||
| 160 | 4 | F32 | `SurfaceRumbleRearRight` |
|
||
|
||
#### 第 11 部分:轮胎滑移角(16 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 |
|
||
|------|------|------|------|
|
||
| 164 | 4 | F32 | `TireSlipAngleFrontLeft` |
|
||
| 168 | 4 | F32 | `TireSlipAngleFrontRight` |
|
||
| 172 | 4 | F32 | `TireSlipAngleRearLeft` |
|
||
| 176 | 4 | F32 | `TireSlipAngleRearRight` |
|
||
|
||
#### 第 12 部分:轮胎复合滑移(16 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 |
|
||
|------|------|------|------|
|
||
| 180 | 4 | F32 | `TireCombinedSlipFrontLeft` |
|
||
| 184 | 4 | F32 | `TireCombinedSlipFrontRight` |
|
||
| 188 | 4 | F32 | `TireCombinedSlipRearLeft` |
|
||
| 192 | 4 | F32 | `TireCombinedSlipRearRight` |
|
||
|
||
#### 第 13 部分:实际悬挂行程(16 字节,单位:米)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 |
|
||
|------|------|------|------|
|
||
| 196 | 4 | F32 | `SuspensionTravelMetersFrontLeft` |
|
||
| 200 | 4 | F32 | `SuspensionTravelMetersFrontRight` |
|
||
| 204 | 4 | F32 | `SuspensionTravelMetersRearLeft` |
|
||
| 208 | 4 | F32 | `SuspensionTravelMetersRearRight` |
|
||
|
||
#### 第 14 部分:车辆信息(20 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 212 | 4 | S32 | `CarOrdinal` | 车辆唯一ID |
|
||
| 216 | 4 | S32 | `CarClass` | 等级 0(D) ~ 7(X) |
|
||
| 220 | 4 | S32 | `CarPerformanceIndex` | 性能指数 100~999 |
|
||
| 224 | 4 | S32 | `DrivetrainType` | 0=FWD, 1=RWD, 2=AWD |
|
||
| 228 | 4 | S32 | `NumCylinders` | 引擎气缸数 |
|
||
|
||
#### 第 15 部分:FH6 新增字段(12 字节)⚠️
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 232 | 4 | U32 | `CarGroup` | 车辆组标识(FH6新增) |
|
||
| 236 | 4 | F32 | `SmashableVelDiff` | 可破坏物体碰撞速度损失(m/s) |
|
||
| 240 | 4 | F32 | `SmashableMass` | 碰撞物体质量(kg) |
|
||
|
||
> ⚠️ **FH5 没有这三个字段**,此位置为 12 字节的占位数据(`HorizonPlaceholder`)
|
||
|
||
#### 第 16 部分:坐标与速度(16 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 244 | 4 | F32 | `PositionX` | 世界坐标 X(米) |
|
||
| 248 | 4 | F32 | `PositionY` | 世界坐标 Y(米) |
|
||
| 252 | 4 | F32 | `PositionZ` | 世界坐标 Z(米) |
|
||
| 256 | 4 | F32 | `Speed` | 速度(m/s) |
|
||
|
||
#### 第 17 部分:动力与温度(24 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 260 | 4 | F32 | `Power` | 功率(瓦特) |
|
||
| 264 | 4 | F32 | `Torque` | 扭矩(牛·米) |
|
||
| 268 | 4 | F32 | `TireTempFrontLeft` | 轮胎温度 |
|
||
| 272 | 4 | F32 | `TireTempFrontRight` | 轮胎温度 |
|
||
| 276 | 4 | F32 | `TireTempRearLeft` | 轮胎温度 |
|
||
| 280 | 4 | F32 | `TireTempRearRight` | 轮胎温度 |
|
||
|
||
#### 第 18 部分:增压与燃油(8 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 284 | 4 | F32 | `Boost` | 涡轮增压值(PSI,高于大气压) |
|
||
| 288 | 4 | F32 | `Fuel` | 燃油量(0.0~1.0) |
|
||
|
||
#### 第 19 部分:里程与圈时(28 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 292 | 4 | F32 | `DistanceTraveled` | 总行驶距离(米) |
|
||
| 296 | 4 | F32 | `BestLap` | 最佳圈时(秒) |
|
||
| 300 | 4 | F32 | `LastLap` | 上一圈用时(秒) |
|
||
| 304 | 4 | F32 | `CurrentLap` | 当前圈用时(秒) |
|
||
| 308 | 4 | F32 | `CurrentRaceTime` | 总比赛时间(秒) |
|
||
|
||
#### 第 20 部分:比赛信息(6 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 312 | 2 | U16 | `LapNumber` | 已完成圈数 |
|
||
| 314 | 1 | U8 | `RacePosition` | 当前排名位置 |
|
||
|
||
#### 第 21 部分:玩家输入(6 字节,值范围 0~255)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 315 | 1 | U8 | `Accel` | 油门(0~255) |
|
||
| 316 | 1 | U8 | `Brake` | 刹车(0~255) |
|
||
| 317 | 1 | U8 | `Clutch` | 离合器(0~255) |
|
||
| 318 | 1 | U8 | `HandBrake` | 手刹(0~255) |
|
||
| 319 | 1 | U8 | `Gear` | 当前档位 |
|
||
|
||
#### 第 22 部分:转向与AI(3 字节)
|
||
|
||
| 偏移 | 大小 | 类型 | 字段 | 说明 |
|
||
|------|------|------|------|------|
|
||
| 320 | 1 | S8 | `Steer` | 转向(-127左 ~ 0中 ~ 127右) |
|
||
| 321 | 1 | S8 | `NormalizedDrivingLine` | 归一化赛道线(-127~127) |
|
||
| 322 | 1 | S8 | `NormalizedAIBrakeDifference` | AI刹车差异(-127~127) |
|
||
|
||
---
|
||
|
||
### 快速参考:字段总数
|
||
|
||
- **FH6**:约 77 个字段,总计 **324 字节**
|
||
- **FH5**:约 74 个字段(比 FH6 少 CarGroup/SmashableVelDiff/SmashableMass),总计 **约 311 字节**
|
||
- **FH4**:和 FH5 基本一致
|
||
|
||
---
|
||
|
||
## Gear 字段的完整取值范围
|
||
|
||
在您看到的包结构中(offset 319, 1字节),`Gear` 字段的实际取值范围比常见的 `1~6` 要大得多:
|
||
|
||
| Gear 值 | 含义 | 出现场景 |
|
||
|---------|------|---------|
|
||
| **0** | **N 档(Neutral,空档)** | 启动引擎未挂档、手动切空档 |
|
||
| **1~10** | 前进档 1~10 档 | 正常行驶 |
|
||
| **11** | **R 档(Reverse,倒车档)** | 倒车时 |
|
||
| **12~20** | **保留/未使用** | 极少数特殊场景 |
|
||
| **21** | **P 档(Park,驻车档)** | 赛车完全静止时极罕见触发 |
|
||
| **22+** | 理论上可能更高(如 D 档标记) | 极少见 |
|
||
|
||
### Gear 值说明
|
||
|
||
这个值在正常游戏中几乎不会出现,因为《极限竞速:地平线》本质上没有"驻车"机制——汽车要么在跑,要么完全静止时引擎仍在空档(Gear=0)。但极少数情况下(如某些特殊漂移状态下),游戏引擎会短暂报告 Gear=21。
|
||
|
||
### N 档的显示逻辑
|
||
|
||
N 档(Gear=0)相对常见,但游戏中 **UI 通常不会显示 "N"**,而是在仪表盘上显示 **"--"** 或空白。只有一些第三方遥测仪表才将其明确标注为 "N"。
|
||
|
||
---
|
||
|
||
## 完整 Gear 映射表(社区共识)
|
||
|
||
| 原始值 (U8) | FH4/FH5 UI 显示 | FH6 UI 显示 | 实际含义 |
|
||
|------------|----------------|------------|---------|
|
||
| 0 | `N` 或 `--` | `N` 或 `--` | 空档 (Neutral) |
|
||
| 1 | `1` | `1` | 1档 |
|
||
| 2 | `2` | `2` | 2档 |
|
||
| 3 | `3` | `3` | 3档 |
|
||
| 4 | `4` | `4` | 4档 |
|
||
| 5 | `5` | `5` | 5档 |
|
||
| 6 | `6` | `6` | 6档 |
|
||
| 7 | `7` | `7` | 7档 |
|
||
| 8 | `8` | `8` | 8档 |
|
||
| 9 | `9` | `9` | 9档(极少车辆有) |
|
||
| 10 | `10` | `10` | 10档(极少车辆有) |
|
||
| **11** | `R` | `R` | 倒车档 (Reverse) |
|
||
| **21** | `P` | `P` | **驻车档 (Park)**(极罕见) |
|
||
| 255 | `?` | `?` | 无效/初始化状态 |
|
||
|
||
---
|
||
|
||
## 为什么您很少见到 P 档?
|
||
|
||
原因有两个:
|
||
|
||
1. **FH 系列赛车游戏没有"驻车"机制**——汽车没有手刹锁止的"P"状态,手刹是 `HandBrake` 字段单独控制
|
||
2. **Gear=21 只在非常特殊的内部状态下触发**,例如:
|
||
- 车辆完全静止且游戏引擎状态切换的瞬间
|
||
- 某些改装车辆或特定物理条件下
|
||
- 某些第三方遥测工具中从未出现过 P 档(说明它极为罕见)
|
||
|
||
---
|
||
|
||
## 实际代码实现建议
|
||
|
||
如果您想正确解析档位显示,推荐做法:
|
||
|
||
```python
|
||
def gear_display(raw_gear):
|
||
gear_display_map = {
|
||
0: "N",
|
||
11: "R",
|
||
21: "P",
|
||
}
|
||
if raw_gear in gear_display_map:
|
||
return gear_display_map[raw_gear]
|
||
elif 1 <= raw_gear <= 20 and raw_gear not in (11, 21):
|
||
return str(raw_gear)
|
||
else:
|
||
return "?"
|
||
```
|
||
|
||
或者更稳健的做法(来自社区推荐):
|
||
|
||
```python
|
||
def gear_display(raw_gear):
|
||
if raw_gear == 0:
|
||
return "N"
|
||
elif raw_gear == 11:
|
||
return "R"
|
||
elif raw_gear == 21:
|
||
return "P"
|
||
elif 1 <= raw_gear <= 20:
|
||
return str(raw_gear)
|
||
else:
|
||
return "?"
|
||
```
|