3 分钟跑通 @pmndrs/racing-game:React + Three.js 赛车游戏逐层拆解
【免费下载链接】racing-game🏎 Open source racing game developed by everyone willing项目地址: https://gitcode.com/gh_mirrors/ra/racing-game
@pmndrs/racing-game 是一个浏览器赛车游戏:React 写逻辑,Three.js 画画面,cannon-es 管物理。下面按“先看画面、再拆代码”的顺序讲。
🎬 先看清画面:低多边形小车跑在 3D 沙漠赛道上
一辆低多边形小车在起伏的沙漠赛道上加速、漂移,身后扬起尘土;右下角小地图跟着车转,踩氮气还有特写。场景里只有一片天空和一盏方向光,没有高清贴图,每一样东西都能找到对应代码。
三步跑通本地环境
git clone https://gitcode.com/gh_mirrors/ra/racing-game && cd racing-game yarn install yarn devVite 几秒内起本地服务,默认 5173 端口,方向键即可开走。
按画面拆解:车、赛道、HUD 与音效
小车为什么能跑:useFrame 帧循环加射线车辆
小车不播动画。src/models/vehicle/Vehicle.tsx 里的 useFrame 钩子是游戏的心跳,每渲染一帧跳一次:读按键、给后轮施力、算刹车转向,再把相机插值挪到车后。轮子抓地由 useRaycastVehicle 处理,四个轮子像弹簧探针戳地面算摩擦,物理不用自己写。
useFrame((state, delta) => { // 每帧:施力、转向、刹车、挪相机 })赛道是怎么由一张灰度图生成的
赛道主体是加载 GLB 后逐 mesh 摆位(src/models/track/Track.tsx)。边缘的起伏地形是“画”出来的:src/models/track/Heightmap.tsx 把每个像素的亮度乘上系数变成高度,交给 useHeightfield 生成碰撞体。灰度即等高线:白是山,黑是谷。
速度表、小地图与排行榜从哪来
HUD 是叠在 Canvas 外的普通 HTML 组件。速度表每帧从 zustand 共享状态读速度;小地图的巧思在 src/ui/Minimap.tsx:场景里多塞一台正交相机,只渲染“关卡层”物体,每帧输出画到角落小画布——同一份场景白捡一张俯视图。排行榜前三名显示奖牌(src/ui/LeaderBoard.tsx):
音效如何跟着车速变
public/sounds/ 里有引擎、刹车等 8 段音频。src/effects/audio/Engine.tsx 用 PositionalAudio 把声音挂到 3D 坐标,车远声小,音量再和车速挂钩,加速时轰鸣自然“爬升”。
3 个可迁移的设计点
用 React 组件描述 3D 场景。整个 3D 世界是一棵 JSX 树,车、赛道、相机都是组件;react-three/fiber 让卸载即销毁对象、props 变化即更新参数。写 Three.js 交互原型时,可以先按组件拆场景,调试直接看组件树。
输入与逻辑分离。src/controls/Keyboard.ts 只把按键翻译成“前进/刹车/转向”写进 zustand store,Vehicle 只读 store 不碰事件。加手柄或触屏,再写一个写同一 store 的输入源即可。
物理与渲染分层。碰撞在 cannon-es 里算,帧循环只负责把算好的位置画出来,再补相机跟随、车身倾斜这类“演出”。分工在 src/store.ts 的共享状态里最清楚:渲染卡顿不会拖乱物理。
带走骨架:下一步从 src/App.tsx 读起
你能带走的是“组件树 + 独立物理 + 共享状态”这套 3D 游戏骨架。先读 src/App.tsx 看场景和 HUD 怎么摆进 Canvas,再进 Vehicle.tsx 盯一遍 useFrame,一个 react-three/fiber 项目就算通读。
【免费下载链接】racing-game🏎 Open source racing game developed by everyone willing项目地址: https://gitcode.com/gh_mirrors/ra/racing-game
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考