
《极速狂飙》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_FL、wheel_FR等 - 为前轮创建转向包装器
- 统一缩放适配
八、部署说明
8.1 环境要求
- 现代浏览器(支持 ES Module)
- 支持 WebGL 2.0
8.2 启动方式
# 使用任意HTTP服务器
npx serve .
# 或
python -m http.server
8.3 资源说明
models/目录存放 GLB 模型文件(需自行添加)- 所有纹理均为程序化生成,无需外部图片资源
九、总结
《极速狂飙》是一个架构清晰、模块化的 3D 赛车游戏,具有以下特点:
- 模块化设计:各功能模块职责单一,便于维护和扩展
- 性能优化:使用 InstancedMesh、阴影优化等技术保证流畅运行
- 可扩展性:地图、车型、控制方式均可轻松扩展
- 完整的游戏体验:包含物理引擎、圈速计时、多视角、特效等完整功能
- 跨平台支持:键盘和手柄双模式控制
《极速狂飙》通过模块化设计、性能优化和可扩展性设计,提供了一个完整的游戏体验,支持键盘和手柄双模式控制,具有很高的可玩性和趣味性。