从零构建黑客帝国VR系统:React + Three.js + Socket.IO 全栈架构实战
一个完整的 3D 多人虚拟现实系统,从场景渲染到实时对战,53 个功能模块的技术拆解。
缘起
黑客帝国(The Matrix)的绿色数字雨、子弹时间、Agent Smith 的冷酷追击——这些元素一直让我着迷。作为一个全栈开发者,我决定用 Web 技术亲手复刻它。
三个月后,一个基于 React 19 + Three.js 0.184 + Socket.IO 4 + SQLite 的完整 VR 系统诞生了。这篇文章将拆解它的技术架构。
一、技术选型:为什么是这套组合?
| 层 | 技术 | 理由 |
|---|---|---|
| 渲染 | Three.js 0.184 | WebGL 事实标准,社区庞大 |
| 框架 | React 19 + Vite 8 | HMR 秒级热更新,TSX 写 3D 逻辑 |
| 物理 | cannon-es | 轻量级,Tree-shaking 友好 |
| 实时通信 | Socket.IO 4 | 房间/广播/断线重连开箱即用 |
| 数据库 | SQLite (better-sqlite3) | 零配置,单文件,适合中小项目 |
| 部署 | Docker + docker-compose | 一键启动,环境隔离 |
为什么不选 Unity/Unreal?
- Web 端零安装,一个链接即可体验
- 前端生态丰富,UI/特效开发效率极高
- TypeScript 端到端类型安全
二、项目结构:monorepo 的分层设计
vr-system/
├── client/ # React + Three.js 前端
│ ├── src/
│ │ ├── ai/ # AI 系统(20+ 文件)
│ │ │ ├── BehaviorTree.ts # 9 种行为预设
│ │ │ ├── SwarmTactics.ts # 蜂群战术
│ │ │ ├── EmotionSystem.ts # 情绪传播
│ │ │ └── AdaptiveAgent.ts # 自适应代理
│ │ ├── systems/ # 游戏系统
│ │ │ ├── CombatSystem.ts # 战斗/射击
│ │ │ ├── VehicleSystem.ts # 载具驾驶
│ │ │ └── PerformanceUtils.ts # 视锥剔除/LOD
│ │ ├── effects/ # 特效系统
│ │ │ ├── ParticleEffects.ts # 爆炸/烟雾/弹道
│ │ │ └── SoundEffects3D.ts # Web Audio 程序化音效
│ │ ├── hooks/ # 可复用 Hook
│ │ │ ├── useAIKeyboard.ts # 快捷键管理
│ │ │ └── useSceneCore.ts # 场景初始化
│ │ └── scenes/ # 场景(16 个)
│ │ ├── AdvancedAIScene.tsx # 主 AI 场景
│ │ ├── PvPArena.tsx # PvP 竞技场
│ │ └── TrainingGround.tsx # 射击训练场
├── server/ # Express + Socket.IO 后端
│ └── src/
│ ├── index.ts # 主入口 + 匹配队列
│ ├── PvPManager.ts # PvP 伤害/击杀同步
│ └── RoomManager.ts # 房间管理
└── docs/ # 32 个 .md 文档
核心原则:单一职责
每个系统独立成文件,通过接口交互。战斗系统不关心 AI,AI 不关心渲染——只在 AdvancedAIScene 中编排。
三、场景渲染:Three.js + React 的共存之道
很多人疑惑:React 的声明式 UI 和 Three.js 的命令式渲染如何共存?
我们的方案:React 管理 UI 层,Three.js 独占一个 <div> 的 Canvas。
function AdvancedAIScene() {
const mountRef = useRef<HTMLDivElement>(null)
useEffect(() => {
// Three.js 完全自主管理这个 div
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, ...)
const renderer = new THREE.WebGLRenderer()
mountRef.current!.appendChild(renderer.domElement)
// 动画循环
const loop = () => {
requestAnimationFrame(loop)
updateAISystems(deltaTime)
renderer.render(scene, camera)
}
loop()
}, [])
return (
<div style={{ width: '100%', height: '100vh' }}>
{/* Three.js Canvas */}
<div ref={mountRef} style={{ width: '100%', height: '100%' }} />
{/* React UI 叠加层 */}
<Minimap data={minimapData} />
<InventoryHUD slots={invSlots} />
</div>
)
}
关键经验:
- Three.js 的
useEffect([], [])只挂载一次,动画循环用requestAnimationFrame - Rerender 需要的数据通过
useRef而非useState,避免每帧触发 React diff - UI 组件(HUD、小地图)用 React 渲染在 Canvas 上层,z-index 分层
四、实时通信:Socket.IO 房间 + PvP 同步
服务端核心是一个 Socket.IO Server + 房间管理器:
// 匹配大厅:模块级共享队列
const matchQueue: Map<string, MatchEntry> = new Map()
const checkMatchStart = () => {
const readyPlayers = [...matchQueue.values()].filter(e => e.ready)
if (readyPlayers.length >= 2) {
const [p1, p2] = readyPlayers.slice(0, 2)
p1.socket.emit('match-start', { id: p2.id, name: p2.name })
p2.socket.emit('match-start', { id: p1.id, name: p1.name })
}
}
// PvP 伤害同步
socket.on('player-hit', (data) => {
const target = pvpManager.getPlayer(data.targetId)
target.hp -= data.damage
io.to(roomId).emit('player-damaged', {
targetId: data.targetId,
targetHp: target.hp
})
})
踩坑记录:
matchQueue必须定义在io.on('connection')外部,否则每个连接有独立队列- PvP 玩家不经过
player-join注册,需要单独的PvPManager管理 - 客户端 HUD 用
useRef而非useState读血量,否则闭包陈旧值不更新
五、性能优化:视锥剔除 + 对象池
5000+ 行主场景组件,30+ AI 代理同时运行,如何保持 60fps?
// FrustumCuller: 复用投影矩阵,批量过滤
class FrustumCuller {
update() {
this.projMatrix.multiplyMatrices(
camera.projectionMatrix,
camera.matrixWorldInverse
)
this.frustum.setFromProjectionMatrix(this.projMatrix)
}
isVisible(obj: Object3D, radius = 2): boolean {
this.sphere.set(obj.position, radius)
return this.frustum.intersectsSphere(this.sphere)
}
}
// 在动画循环中:
culler.update()
for (const agent of agents.values()) {
if (!culler.isVisible(agent.mesh, 3)) {
agent.mesh.visible = false // 跳过 GPU 绘制
}
}
效果:30 个代理中平均 8-12 个在屏幕外被剔除,GPU 负载降低 ~30%。
六、工程化:从 0 到 v3.5 的迭代历程
| 版本 | 里程碑 | 核心产物 |
|---|---|---|
| v1 | 基础渲染 | Three.js 场景 + 数字雨 |
| v2 | AI 系统 | 行为树(9) + 蜂群(6) + 小队/指挥 |
| v3 | 多人+玩法 | Socket.IO 房间 + 战斗/背包/技能/天气 |
| v3.3 | 高级 AI | 情绪/编队/通信/战术/个性(5) |
| v3.5 | PvP+联机 | 匹配大厅 + 实时对战 + 载具 + 训练场 |
心得:
- 不要过早优化。先用
useState跑通逻辑,确定瓶颈后再切useRef - 特效系统做好可见性优先——默认最大尺寸、最亮颜色,用户看不清再调
- 射线检测必须设置
raycaster.camera,否则 Sprite 会导致 JS 崩溃
总结
这个项目证明了:Web 技术栈完全可以构建媲美原生体验的 3D 多人游戏。
完整源码:VR-System
如果你也在用 Three.js 或 WebGL 做项目,欢迎交流!
📸 截图清单
- 大厅页面 — 展示导航栏和所有场景入口
- 高级AI场景 — 3D人物+数字雨+控制面板
- 项目结构目录树 —
tree -L 2截图
标签:Three.js React Socket.IO WebGL 全栈 游戏开发