《极速狂飙》3D赛车游戏系统设计


一、项目概述

1.1 项目背景

《极速狂飙》是一款基于 Three.js 构建的 3D 赛车游戏,支持多地图切换、多种车型选择、键盘/手柄双模式控制,具有完整的物理引擎、实时 HUD 显示和粒子特效系统。

1.2 技术栈

技术 版本/说明
Three.js r0.160.0
语言 ES6 Modules (JavaScript)
构建工具 原生模块导入
依赖库 jQuery, FontAwesome, Google Fonts

二、系统架构

2.1 整体架构图

┌─────────────────────────────────────────────────────────────────┐
│                        index.html                              │
│                    (HTML + CSS 入口)                          │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                      game-main.js                              │
│                    (主程序入口)                                │
├─────────────────────────────────────────────────────────────────┤
│  • Three.js 场景初始化                                         │
│  • 游戏状态管理                                               │
│  • 主循环 (animate)                                           │
│  • 模块组装与协调                                             │
└─────────────────────────────────────────────────────────────────┘
│              │              │              │
▼              ▼              ▼              ▼
┌───────────┐  ┌───────────┐  ┌───────────┐  ┌───────────┐
│  地图系统  │  │  车辆系统  │  │  物理引擎  │  │  控制系统  │
├───────────┤  ├───────────┤  ├───────────┤  ├───────────┤
│• 地图注册  │  │• 车辆加载  │  │• 速度计算  │  │• 键盘控制  │
│• 地图构建  │  │• 模型替换  │  │• 转向物理  │  │• 手柄控制  │
│• 场景元素  │  │• 车轮识别  │  │• 圈数检测  │  │• 控制切换  │
└───────────┘  └───────────┘  └───────────┘  └───────────┘
│              │              │              │
▼              ▼              ▼              ▼
┌───────────┐  ┌───────────┐  ┌───────────┐  ┌───────────┐
│  视觉系统  │  │  特效系统  │  │  HUD系统   │  │  音频系统  │
├───────────┤  ├───────────┤  ├───────────┤  ├───────────┤
│• 相机控制  │  │• 粒子特效  │  │• 速度表   │  │• 引擎音效  │
│• 天空盒   │  │• 轮胎痕迹  │  │• 圈速显示  │  │• 音效开关  │
│• 光影系统  │  │• 烟雾/火花 │  │• 小地图   │  │           │
└───────────┘  └───────────┘  └───────────┘  └───────────┘

2.2 目录结构

zcore/
├── game-main.js              # 主程序入口
├── game-main.css             # 全局样式
├── modules/
│   ├── maps/                 # 地图系统
│   │   ├── index.js          # 地图注册表
│   │   ├── map-builder.js    # 地图构建工具库
│   │   ├── maps-city.js      # 城市地图
│   │   ├── maps-forest.js    # 森林地图
│   │   ├── maps-valley.js    # 山谷地图
│   │   ├── maps-desert.js    # 沙漠地图
│   │   └── maps-grassland.js # 草原地图
│   ├── car-loader.js         # 车辆加载
│   ├── car-models.js         # 车型配置
│   ├── car-physics.js        # 物理引擎
│   ├── controls-keyboard.js  # 键盘控制
│   ├── controls-gamepad.js   # 手柄控制
│   ├── page-camera.js        # 相机控制
│   ├── page-hud.js           # HUD显示
│   ├── page-audio.js         # 音频系统
│   ├── show-particles.js     # 粒子特效
│   ├── performance-monitor.js# 性能监控
│   └── utils.js              # 工具函数
plugins/
├── js/                       # 第三方库
├── css/                      # 第三方样式

三、核心模块详细设计

3.1 地图系统 (Maps)

3.1.1 架构设计

┌─────────────────────────────────────────────┐
│            maps/index.js                    │
│         (地图注册中心)                      │
├─────────────────────────────────────────────┤
│  getMapList()    → 获取所有地图元信息      │
│  getMap(id)      → 获取地图完整配置        │
│  getMapConfig(id)→ 获取地图参数配置        │
│  buildMap(scene, id)→ 构建地图场景         │
└─────────────────────────────────────────────┘

┌───────────────┼───────────────┬───────────────┐
▼               ▼               ▼               ▼
┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐
│ city.js │  │forest.js│  │desert.js│  │valley.js│
│ 城市夜战 │  │ 森林山道 │  │ 沙漠狂飙 │  │ 山谷疾驰 │
└─────────┘  └─────────┘  └─────────┘  └─────────┘


┌─────────────────────────────────────────────┐
│          map-builder.js                     │
│        (地图构建工具库)                     │
├─────────────────────────────────────────────┤
│  • initTrackCurve()   - 赛道曲线生成       │
│  • buildTrackSurface()- 路面构建           │
│  • buildCurbs()       - 路肩构建           │
│  • buildTrees()       - 树木构建(Instanced)│
│  • buildGround()      - 地面纹理生成       │
│  • buildRocks()       - 岩石构建           │
│  • buildTrackLights() - 路灯构建           │
│  • buildHills()       - 山丘构建           │
└─────────────────────────────────────────────┘

3.1.2 地图数据模型

// 每个地图模块导出标准接口
export const id = 'city';                    // 唯一标识
export const name = '城市夜战';              // 显示名称
export const emoji = '🌃';                  // 图标
export const description = '霓虹灯下的城市赛道'; // 描述

export const config = {
  trackWidth: 12,      // 赛道宽度
  totalLaps: 12,       // 总圈数
  maxSpeed: 70,        // 最高速度 (km/h)
  enableFlatShading: false, // 是否低多边形风格
};

export const theme = {
  sky: { top, mid, bottom },  // 天空颜色
  ground: { color1, color2 }, // 地面颜色
  road: { baseColor, stripeColor }, // 路面颜色
  curb: { color1, color2 },   // 路肩颜色
  tree: { trunk, leaf },      // 树木颜色
  fog: { color, density },    // 雾效
};

export const controlPoints = []; // CatmullRomCurve3 控制点

3.1.3 性能优化设计

地图系统采用多种性能优化策略:

优化技术 应用场景 性能提升
InstancedMesh 树木、岩石、栅栏、花朵 减少 Draw Calls 80%+
BufferGeometry 路面、路肩 高效的顶点数据处理
Canvas 纹理生成 路面纹理、地面纹理 减少外部资源加载
阴影优化 PCFShadowMap 替代 PCFSoftShadowMap 性能提升 30-40%
LOD 思想 复杂场景元素使用简化几何 保持视觉质量同时优化

3.2 物理引擎 (Car Physics)

3.2.1 物理模型

┌─────────────────────────────────────────────────────┐
│                  物理引擎核心                        │
├─────────────────────────────────────────────────────┤
│                                                     │
│  输入: keys (方向/油门/刹车/漂移)                   │
│                                                     │
│  状态更新:                                          │
│  ┌─────────────────────────────────────────────┐    │
│  │  1. 加速度计算 (油门/刹车)                  │    │
│  │  2. 摩擦力衰减 (赛道/草地/漂移)             │    │
│  │  3. 速度限制 (赛道/草地)                    │    │
│  │  4. 转向计算 (速度相关+漂移系数)            │    │
│  │  5. 位置更新 (欧拉积分)                     │    │
│  │  6. 圈数检测 (象限标记法)                   │    │
│  └─────────────────────────────────────────────┘    │
│                                                     │
│  输出: carState (位置/航向/速度/姿态)              │
│                                                     │
└─────────────────────────────────────────────────────┘

3.2.2 核心物理参数

const CFG = {
  trackWidth: 14,        // 赛道宽度
  maxSpeed: 75,          // 最高速度
  maxReverse: -18,       // 倒车最高速度
  accel: 28,             // 加速度
  brake: 45,             // 刹车减速度
  friction: 0.995,       // 赛道摩擦系数
  grassFriction: 0.98,   // 草地摩擦系数
  turnSpeed: 1.8,        // 转向速度
  driftFriction: 0.998,  // 漂移摩擦系数
  driftTurnMul: 1.6,     // 漂移转向倍率
  totalLaps: 10,         // 总圈数
  cameraLerp: 0.06,      // 相机跟随平滑度
};

3.2.3 圈数检测算法

采用象限标记法检测车辆是否完成一圈:

function checkLap(t) {
  // t 为车辆在赛道上的归一化位置 (0~1)
  quadrantFlags[Math.floor(t * 4) % 4] = true;

  // 当从终点区域(>0.85)跨越到起点区域(<0.15)且所有象限都被访问过
  if (lastTrackT > 0.85 && t < 0.15 && quadrantFlags.every(f => f)) {
    lapCount++;
    // 记录圈速
  }
  lastTrackT = t;
}

3.3 车辆系统

3.3.1 车辆加载流程

┌─────────────────────────────────────────────────────────────┐
│                    车辆加载流程                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. loadModelList() ──► 扫描可用车型                       │
│                          │                                  │
│                          ▼                                  │
│  2. 用户选择车型 ──► 更新CAR_MODELS.current                │
│                          │                                  │
│                          ▼                                  │
│  3. loadGLB() ──► 加载GLTF模型文件                         │
│                          │                                  │
│                          ▼                                  │
│  4. replaceCarModel() ──► 替换场景中的车辆                 │
│                          │                                  │
│                          ▼                                  │
│  5. 车轮识别 ──► 建立车轮引用                              │
│     • 查找 wheel_FL, wheel_FR, wheel_RL, wheel_RR         │
│     • 创建转向包装器 (steer_FL, steer_FR)                 │
│                          │                                  │
│                          ▼                                  │
│  6. 缩放适配 ──► 统一车辆大小                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.3.2 车型配置

// car-models.js
export const CAR_MODELS = {
  current: 'default',
  config: {
    'default': { name: '默认赛车', emoji: '🏎️', axis: 'x', scale: 1.0 },
    'Porsche_911.glb': { name: '保时捷 911', emoji: '🏎️', axis: 'x', scale: 0.9 },
    'xiaomi_su7_ultra.glb': { name: '小米-SU7', emoji: '🏎️', axis: 'z', scale: 1.0 },
    // ...
  },
};

3.4 控制系统

3.4.1 双模式控制架构

┌─────────────────────────────────────────────────────────────┐
│                     控制系统                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────┐    ┌─────────────────┐               │
│  │  键盘控制模块    │    │  手柄控制模块    │               │
│  │ (controls-      │    │ (controls-      │               │
│  │  keyboard.js)   │    │  gamepad.js)    │               │
│  ├─────────────────┤    ├─────────────────┤               │
│  │ • WASD/方向键   │    │ • 左摇杆转向    │               │
│  │ • Space 漂移    │    │ • RT 加速       │               │
│  │ • C 切换视角    │    │ • LT 刹车       │               │
│  │ • R 复位        │    │ • A 漂移        │               │
│  │ • M 音效开关    │    │ • B 切换视角    │               │
│  │ • Esc 暂停      │    │ • 十字键视角    │               │
│  └─────────────────┘    └─────────────────┘               │
│           │                        │                       │
│           └──────────┬─────────────┘                       │
│                      ▼                                      │
│           ┌─────────────────────┐                          │
│           │    keys 统一状态     │                          │
│           │   {up, down, left,  │                          │
│           │    right, space}    │                          │
│           └─────────────────────┘                          │
│                      │                                      │
│                      ▼                                      │
│           ┌─────────────────────┐                          │
│           │  物理引擎更新        │                          │
│           └─────────────────────┘                          │
└─────────────────────────────────────────────────────────────┘

3.4.2 控制模式切换

// 用户可随时切换控制模式
function setControlMode(mode) {
  currentControlMode = mode;
  // 手柄未连接时自动降级到键盘
  if (mode === 'gamepad' && !checkGamepadConnection()) {
    showToast('🎮 请先连接手柄');
    return;
  }
  updateControlModeUI();
}

3.5 视觉系统

3.5.1 相机控制

// 五种预设视角
const viewPresets = {
  chase:  { phi: 0.32, dist: 25 },   // 追踪视角
  close:  { phi: 0.22, dist: 10 },   // 近跟视角
  top:    { phi: 1.45, dist: 50 },   // 俯视视角
  left:   { phi: 0.35, dist: 6, thetaOffset: -Math.PI/2 }, // 左侧视角
  right:  { phi: 0.35, dist: 6, thetaOffset: Math.PI/2 },  // 右侧视角
};

相机特性:

  • 平滑跟随 (Lerp 插值)
  • 速度感应 FOV 变化 (60°~80°)
  • 鼠标拖拽旋转
  • 滚轮缩放

3.5.2 光影系统

┌─────────────────────────────────────────────────────────────┐
│                    光影系统                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ☀️ 主光源 (DirectionalLight)                              │
│     • 位置: (60, 100, 40)                                  │
│     • 强度: 1.6                                            │
│     • 阴影: 1024x1024 阴影贴图                             │
│                                                             │
│  🌤 环境光 (AmbientLight)                                  │
│     • 强度: 0.65                                           │
│                                                             │
│  🌈 半球光 (HemisphereLight)                               │
│     • 天空色: #87ceeb                                      │
│     • 地面色: #4a8a3a                                      │
│     • 强度: 0.5                                            │
│                                                             │
│  🌫 雾效 (FogExp2)                                         │
│     • 密度: 0.0010~0.003 (地图自适应)                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.6 特效系统

3.6.1 粒子系统

特效类型 触发条件 视觉效果
排气管烟雾 加速中(速度 2~35) 灰色半透明烟雾
漂移烟雾 漂移中且速度 > 15 大量白色烟雾
草地碎屑 车辆在草地上且速度 > 10 绿色/棕色碎片
刹车火花 刹车且速度 > 40 橙色闪光粒子
轮胎发热 高速行驶(速度 > 45) 橙色半透明热浪

3.6.2 轮胎痕迹系统

// 使用 LineSegments 实现轮胎痕迹
const MAX_TRAIL_POINTS = 2000;
// 在漂移时记录左右后轮位置
// 每帧最多添加2个点,形成两条轨迹

3.7 HUD 系统

3.7.1 界面布局

┌─────────────────────────────────────────────────────────────┐
│  [控制面板]  [暂停]  [圈速信息]                           │
│  视角切换      ⏸     圈数: 0/10                           │
│  控制模式            当前: 00:00.000                      │
│  音效/复位           最佳: --:--.---                      │
│                      总时: 00:00.000                      │
│                                                             │
│                    [游戏画面]                              │
│                                                             │
│  [速度表]          [路面指示]    [小地图]                 │
│  000 KM/H          赛道/草地                              │
│  ████████░░                                            │
│  N                                                         │
└─────────────────────────────────────────────────────────────┘

四、数据流设计

4.1 主循环数据流

┌─────────────────────────────────────────────────────────────────────┐
│                           主循环 (animate)                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  输入: time (毫秒)                                                 │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ 1. 计算 deltaTime                                          │   │
│  │ 2. 更新游戏状态                                            │   │
│  │    ├── 键盘/手柄输入 → keys                               │   │
│  │    ├── 物理更新 → carState                                │   │
│  │    ├── 车身姿态 → rollAngle, pitchAngle                   │   │
│  │    ├── 粒子特效 → particles                               │   │
│  │    └── 圈数检测 → lapCount, lapStats                      │   │
│  │ 3. 更新渲染                                              │   │
│  │    ├── 车辆位置/旋转 → carYawGroup, carLeanGroup         │   │
│  │    ├── 相机跟随 → camera                                 │   │
│  │    ├── 轮胎痕迹 → trailLine                              │   │
│  │    └── 音频更新 → engineOsc                              │   │
│  │ 4. HUD更新                                               │   │
│  │    ├── 速度表 → speedometer                              │   │
│  │    ├── 圈速信息 → lap-info                               │   │
│  │    └── 小地图 → minimap-canvas                           │   │
│  │ 5. 渲染场景 → renderer.render(scene, camera)             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  输出: 更新后的游戏状态和渲染画面                                  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4.2 状态管理

┌─────────────────────────────────────────────────────────────────────┐
│                        游戏状态机                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────┐  点击开始   ┌────────────┐  倒计时结束  ┌──────────┐ │
│  │  MENU   │ ──────────► │  COUNTDOWN │ ──────────► │ PLAYING  │ │
│  └─────────┘             └────────────┘             └──────────┘ │
│       ▲                        │                        │         │
│       │                        │                        │         │
│       │                        │                        ▼         │
│       │                   ┌────▼────┐  按Esc/暂停按钮  ┌────────┐ │
│       │                   │ PAUSED  │ ◄─────────────── │ PLAYING│ │
│       │                   └─────────┘                  └────────┘ │
│       │                        │                        │         │
│       │                        │                        ▼         │
│       │                   ┌────▼────┐  完成所有圈数   ┌──────────┐ │
│       └───────────────────│ FINISHED│ ◄────────────── │ PLAYING  │ │
│                           └─────────┘                  └──────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

五、性能优化策略

5.1 渲染优化

优化项 实现方式 效果
InstancedMesh 树木、岩石、栅栏、花朵批量渲染 Draw Calls ↓80%
PCFShadowMap 替代 PCFSoftShadowMap GPU 负载 ↓30%
Canvas 纹理 程序化生成纹理 减少网络请求
几何体复用 共享几何体/材质 内存占用 ↓
阴影优化 关闭部分物体阴影投射 渲染负载 ↓

5.2 内存优化

┌─────────────────────────────────────────────────────────────────────┐
│                       内存管理策略                                 │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. 地图切换时清理                                                │
│     • scene.remove() 移除旧地图物体                               │
│     • geometry.dispose() 释放几何体内存                           │
│     • material.dispose() 释放材质内存                             │
│                                                                     │
│  2. 纹理管理                                                     │
│     • Canvas纹理生成,无需外部图片                                │
│     • 纹理复用 (共享纹理)                                        │
│                                                                     │
│  3. 车辆加载                                                      │
│     • 按需加载模型文件                                            │
│     • 模型缓存 (后续可扩展)                                      │
│                                                                     │
│  4. 性能监控                                                      │
│     • FPS监控 (按F9显示)                                         │
│     • 内存使用监控 (performance.memory)                           │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

六、扩展性设计

6.1 新增地图

只需在 maps/ 目录下创建新文件:

// maps/map-new.js
export const id = 'new';
export const name = '新赛道';
export const emoji = '🏁';
// ... 配置和构建函数

export default { id, name, emoji, description, config, theme, controlPoints, build };

然后在 maps/index.js 中导入并注册。

6.2 新增车型

car-models.js 中添加配置:

'New_Car.glb': {
  name: '新车',
  emoji: '🚗',
  axis: 'x',
  scale: 1.0,
}

6.3 新增控制方式

  • 创建新的控制模块(如 controls-touch.js
  • 实现统一的 keys 输出接口
  • game-main.js 中集成切换逻辑

七、技术难点与解决方案

7.1 赛道曲线生成

问题: 需要生成平滑的闭环赛道

解决方案: 使用 THREE.CatmullRomCurve3 生成平滑曲线

const curve = new THREE.CatmullRomCurve3(controlPoints, true, 'catmullrom', 0.5);

7.2 车辆与赛道对齐

问题: 车辆需要始终保持在赛道平面上

解决方案:

  • 车辆位置在 Y 轴上固定为 0
  • 通过 getClosestT() 计算车辆在赛道上的最近点
  • 用于圈数检测和路面判断

7.3 多车型兼容

问题: 不同 GLB 模型的车轮命名和层级结构不同

解决方案:

  • 遍历模型树查找标准命名节点
  • 自动识别 wheel_FLwheel_FR
  • 为前轮创建转向包装器
  • 统一缩放适配

八、部署说明

8.1 环境要求

  • 现代浏览器(支持 ES Module)
  • 支持 WebGL 2.0

8.2 启动方式

# 使用任意HTTP服务器
npx serve .
# 或
python -m http.server

8.3 资源说明

  • models/ 目录存放 GLB 模型文件(需自行添加)
  • 所有纹理均为程序化生成,无需外部图片资源

九、总结

《极速狂飙》是一个架构清晰、模块化的 3D 赛车游戏,具有以下特点:

  • 模块化设计:各功能模块职责单一,便于维护和扩展
  • 性能优化:使用 InstancedMesh、阴影优化等技术保证流畅运行
  • 可扩展性:地图、车型、控制方式均可轻松扩展
  • 完整的游戏体验:包含物理引擎、圈速计时、多视角、特效等完整功能
  • 跨平台支持:键盘和手柄双模式控制

《极速狂飙》通过模块化设计、性能优化和可扩展性设计,提供了一个完整的游戏体验,支持键盘和手柄双模式控制,具有很高的可玩性和趣味性。