一个完全运行在浏览器中的 3D 体素沙盒游戏,使用 TypeScript + Three.js 从零构建
2009 年,Markus "Notch" Persson 创造了 Minecraft,这款看似简单的方块游戏彻底改变了游戏行业。它证明了一个道理:游戏的魅力不在于画面的精细程度,而在于它能给予玩家多大的创造自由。十五年后的今天,Minecraft 已经成为史上销量最高的电子游戏,累计销量超过 3 亿份。
那么,如果我们想在浏览器中重现这种体验,需要克服哪些技术挑战?这正是 WebCraft 项目要回答的问题。
WebCraft 是一个完全运行在浏览器中的 3D 体素沙盒游戏。它不需要任何后端服务器,不需要安装任何插件,只需要一个现代浏览器,就能让玩家在无限的方块世界中自由探索、建造和创造。这个项目展示了 Web 技术在游戏开发领域的巨大潜力,也是对"浏览器能做什么"这个问题的一次深度探索。
本文将带你深入 WebCraft 的技术内核,从渲染引擎到物理系统,从地形生成到生态模拟,从音频处理到数据持久化,全面解析这个项目的设计理念和实现细节。
在开始任何项目之前,技术栈的选择都是至关重要的决策。对于一个 3D 游戏来说,这个选择更加关键,因为它直接决定了项目的性能上限和开发效率。
TypeScript 5.6 作为主开发语言是一个经过深思熟虑的选择。JavaScript 虽然是 Web 的原生语言,但它的动态类型特性在大型项目中会带来严重的维护问题。TypeScript 的静态类型系统不仅能在编译期捕获大量错误,还能为 IDE 提供强大的智能提示支持。在 WebCraft 这样拥有超过 100 个源文件、涉及复杂 3D 数学运算的项目中,类型安全是保证代码质量的基石。
// TypeScript 的类型系统让复杂的数据结构一目了然
interface IPhysicsBody {
position: { x: number; y: number; z: number }
velocity: { x: number; y: number; z: number }
width: number
height: number
isGrounded: boolean
isInWater: boolean
}Three.js 0.170 是目前最成熟的 WebGL 3D 渲染库。它封装了底层的 WebGL API,提供了场景图、材质系统、光照模型等高级抽象,让开发者可以专注于游戏逻辑而不是图形学细节。
Vite 6.0 作为构建工具,提供了极快的开发服务器启动速度和热模块替换(HMR)功能。在开发过程中,修改代码后几乎可以立即看到效果,这对于需要频繁调试的游戏开发来说是巨大的效率提升。
除了这三个核心依赖,项目还充分利用了浏览器的原生 API:
- Web Audio API:用于实现 3D 空间音效和程序化音频合成
- IndexedDB:用于存储游戏存档,支持大容量数据持久化
- LocalStorage:用于保存用户偏好设置
- requestAnimationFrame:用于实现稳定的游戏主循环
WebCraft 采用了模块化的架构设计,将游戏的各个子系统清晰地分离:
src/
├── main.ts # 入口文件,初始化游戏
├── core/ # 核心模块
│ ├── Game.ts # 游戏主循环
│ ├── World.ts # 世界管理
│ ├── Chunk.ts # 区块数据结构
│ └── Block.ts # 方块定义(38种)
├── renderer/ # 渲染模块
│ ├── Renderer.ts # Three.js 封装
│ ├── ChunkMesh.ts # 区块网格生成
│ └── CameraController.ts # 相机控制
├── physics/ # 物理系统
│ ├── PhysicsSystem.ts # 物理主系统
│ └── Collision.ts # 碰撞检测
├── terrain/ # 地形生成
│ ├── TerrainGenerator.ts # 地形生成器
│ ├── BiomeGenerator.ts # 生物群系
│ └── NoiseGenerator.ts # Simplex 噪声
├── entities/ # 实体系统(9种动物)
├── audio/ # 音频系统
├── weather/ # 天气与昼夜系统
├── storage/ # 存档系统
└── ui/ # UI 组件
这种架构遵循了单一职责原则:每个模块只负责一个特定的功能领域。例如,PhysicsSystem 只处理物理模拟,不关心渲染;ChunkMesh 只负责生成网格,不关心区块数据如何存储。
游戏的心脏是主循环(Game Loop)。WebCraft 使用 requestAnimationFrame 实现了一个稳定的主循环:
private gameLoop(): void {
requestAnimationFrame(() => this.gameLoop())
const now = performance.now()
const deltaTime = Math.min((now - this.lastFrameTime) / 1000, 0.1)
this.lastFrameTime = now
// 1. 更新时间系统(昼夜循环)
this.timeSystem.update(deltaTime)
// 2. 更新天气系统
this.weatherSystem.update(deltaTime)
// 3. 更新物理系统
this.physicsSystem.update(deltaTime)
// 4. 更新实体(动物 AI)
this.entityManager.update(deltaTime, playerPosition, world)
// 5. 更新区块加载
this.chunkManager.update(playerX, playerY, playerZ)
// 6. 渲染场景
this.renderer.render(this.scene, this.camera)
}注意 deltaTime 的计算:我们限制最大值为 0.1 秒,防止页面失去焦点后恢复时出现"时间跳跃"导致的物理异常。
WebCraft 的开发采用了规格驱动的迭代开发模式。每个功能都有完整的规格文档,定义了用户故事、验收标准和技术要求。以下是项目从零到一的完整演进历程:
目标:创建一个可交互的 3D 方块世界原型
这是整个项目的起点。我们建立了游戏的基础框架:第一人称视角(WASD 移动 + 鼠标视角控制)、5 种基础方块类型(草地、泥土、石头、木头、沙子)、左键破坏/右键放置的交互机制,以及一个 32×32 的平坦方块地面。这个迭代验证了核心玩法的可行性——玩家可以在简单的世界中自由移动、破坏和建造。
目标:实现无限世界和程序化地形生成
平坦的世界很快就会让人感到无聊。这个迭代引入了两个关键系统:将世界划分为 16×16×16 的区块单元,支持按需加载和卸载;使用 Simplex 噪声算法生成起伏的自然地形,根据高度自动分布方块类型——表层草地、中层泥土、底层石头;使用 3D 噪声在地下雕刻出可探索的洞穴空间。区块系统是支撑大型世界的基础架构,通过只渲染玩家视野范围内的区块,实现了理论上无限大的世界。
目标:让玩家与世界产生真实的物理交互
没有物理系统,玩家会像幽灵一样穿过所有方块。这个迭代实现了:重力系统(玩家受到恒定的向下加速度)、AABB 碰撞检测(使用轴对齐包围盒检测玩家与方块的碰撞)、分轴解析(分别处理 X、Y、Z 轴的碰撞,实现沿墙滑动效果)、跳跃功能(按空格键跳跃,高度刚好能跳上一个方块)。碰撞检测的核心挑战在于处理玩家同时接触多个方块的情况,我们采用了分轴解析的方法避免了玩家被"卡"在角落的问题。
目标:创建一个令人印象深刻的出生点
一个好的出生点能给玩家留下深刻的第一印象。我们选择了罗马斗兽场作为出生地标:使用参数方程生成 80×60 方块的椭圆形竞技场,3 层递进的阶梯式观众席,环绕建筑的标志性拱门,以及部分区域设计为坍塌状态的废墟效果。斗兽场不仅是视觉地标,也是玩家的重生点。
目标:用真实纹理替代纯色方块
纯色方块虽然能表达基本信息,但缺乏视觉吸引力。这个迭代引入了完整的纹理系统:将所有 16×16 像素的方块纹理合并为纹理图集优化渲染性能;草方块顶部是绿色草地、侧面是带草边的泥土、底部是纯泥土的多面纹理;新增圆石、砖块、玻璃、水、树叶、原木、木板、雪等 8 种方块;正确处理玻璃、水、树叶等透明/半透明方块的渲染顺序。纹理系统让游戏的视觉效果有了质的飞跃。
目标:让每次游戏都有不同的世界
固定的地图很快会失去新鲜感。这个迭代实现了:每次新游戏生成不同的世界种子确保地形的随机性;大片湖泊、连绵山脉、开阔平原,每种地形都有足够的规模感;不同地形类型之间有平滑的渐变过渡;按住 Shift 键以 1.5 倍速度移动的冲刺功能。地形生成使用了分形噪声技术——将多个不同频率和振幅的噪声叠加,创造出既有大尺度地貌又有小尺度细节的自然地形。
目标:修复水体显示问题,增强水下体验
水是游戏中最复杂的元素之一。这个迭代解决了多个水相关的问题:确保水平面以下的所有空间都被水方块正确填充;相邻水方块之间的面不渲染避免视觉混乱;玩家潜入水下时屏幕呈现蓝色色调;水下 16 格以外的物体逐渐衰减不可见。水下效果使用了后处理着色器实现。
目标:让世界充满生机
一个空旷的世界是死寂的。这个迭代引入了动态元素:牛、羊、猪、鸡四种经典动物,具有随机移动、吃草、逃跑等 AI 行为;20 分钟一个游戏日的昼夜循环,太阳东升西落,月亮和星空在夜晚出现;日出橙红、正午湛蓝、日落温暖、夜晚深邃的天空变化;随机出现的降雨天气,雨滴粒子从天空落下。动物使用了简单但有效的状态机 AI,在"闲置"、"漫游"、"逃跑"三种状态之间切换。
目标:扩展动植物种类,控制生成密度
基础生态系统建立后,我们进一步丰富了它:新增兔子、狼、狐狸三种陆地动物以及水中的鱼类;玫瑰、郁金香、雏菊、矢车菊等花卉,橡树、桦树、云杉等树木;每个区块最多 6 只动物、树木最小间距 8 格的密度控制;斗兽场周围 50 格内不生成动物的出生点保护。鱼类的 AI 与陆地动物不同,它们只能在水中移动,遇到水边界时会自动掉头。
目标:提升斗兽场的建筑精美程度
初版斗兽场过于简陋,这个迭代进行了全面的视觉升级:凸出的装饰性壁柱、水平檐口装饰带、顶层女儿墙的立体结构;拱形顶部装饰(拱券石效果)、两侧半圆柱装饰的拱门细节;观众席层次变化、走廊拱形天花板、竞技场地面图案的内部装饰;不规则断裂边缘、散落石块、植被点缀的废墟效果。我们使用了至少 3 种不同材质来区分结构柱、填充墙和装饰元素。
目标:在出生点附近添加更多世界奇观
一座斗兽场不够,我们要打造一个"世界奇观"主题区域:埃及金字塔(经典四面体结构,底边 40 方块,内部有可探索的迷宫走廊和密室);中国故宫(红墙黄瓦、对称布局、多进院落,展现中华古典建筑的庄严);欧洲城堡(高耸塔楼、厚重城墙、护城河,中世纪风格的防御建筑)。金字塔内部的迷宫采用了"中等难度"策略——有死路增加探索感,但主路径用火把标记确保玩家不会完全迷失。
目标:增加听觉反馈和导航工具
沉默的游戏缺乏沉浸感。这个迭代引入了完整的音频和地图系统:循环播放的背景音乐,支持音量调节和静音;脚步声、摔落声、动物叫声,根据距离调整音量的音效系统;右上角圆形实时小地图,显示周围地形和玩家朝向;按 M 键打开全屏大地图,支持缩放查看;左上角实时显示 X、Y、Z 坐标。音频系统使用 Web Audio API 实现,采用程序化音频合成减少资源加载时间。
目标:让玩家能看到自己的角色
第一人称视角虽然沉浸,但玩家看不到自己的角色。这个迭代解决了这个问题:按 V 键在第一人称和第三人称之间切换;第三人称摄像机位于角色身后 5 米,自动避免穿墙;5 种方块人风格的预设角色模型,与游戏世界视觉一致;进入游戏前可预览和选择角色模型的角色选择界面。第三人称摄像机使用射线检测,当摄像机路径上有障碍物时自动拉近距离。
目标:修复视觉问题,提升整体美感
用户反馈指出了几个视觉问题,这个迭代进行了针对性修复:从简单黄色圆形升级为带柔和光晕的自然太阳;将不自然的血红色夜空改为深蓝色/藏青色;黄昏到夜晚的颜色平滑渐变;清晰边框、适中对比度、方向指示的小地图美化。这些看似小的改动,对整体游戏氛围有显著影响。
目标:让操作说明与实际功能保持同步
随着功能的增加,操作面板已经过时。这个迭代更新了所有操作说明:WASD 移动、Space 跳跃、Shift 冲刺的移动控制;1-9,0 选择方块、Tab 循环切换的方块操作;M 地图、V 视角切换、C 角色选择的功能快捷键;Space 上浮、Shift 下潜的水中控制;按逻辑类别分组的操作说明。操作面板是新玩家的第一印象,清晰完整的操作说明能大大降低上手门槛。
目标:确保动物正确生成在地面上
玩家报告动物有时会悬浮在空中。这个迭代深入修复了生成逻辑:向下扫描实际方块数据确定真实地面位置,而非仅依赖理论高度图;检测水方块拒绝在水中生成陆地动物;忽略树叶和树干将它们视为非有效地面;确保动物头部上方有足够的非固体空间。问题的根源在于地形生成使用的高度图与实际方块数据可能不一致(如洞穴、树木等情况)。
目标:为方块交互添加听觉反馈
建造和破坏是游戏的核心操作,却一直缺少音效反馈。这个迭代补齐了这块:将 38 种方块映射到 6 种音效类别(石头、木头、泥土、沙子、玻璃、植物);方块被破坏时播放对应材质的破坏音效;方块被放置时播放对应材质的放置音效;快速连续操作时限制播放频率避免音频过载。音效使用程序化合成而非预录文件,通过调整频率、波形和包络用代码生成各种材质的音效。
目标:让玩家的建造成果能够保存
没有存档功能,所有建造都会在刷新页面后消失。这是最后一个核心功能迭代:使用 IndexedDB 浏览器本地数据库持久化保存世界数据;支持 5 个手动存档槽位,可命名、重命名、删除;方块数据、玩家位置、世界种子全部保存;每 5 分钟自动保存到专用槽位防止意外丢失。存档系统只保存玩家修改过的区块,结合保存的世界种子,加载时可以重新生成未探索区域的地形,大大减少了存储空间需求。
WebCraft 的渲染系统建立在 Three.js 之上:
export class Renderer {
constructor() {
this.renderer = new THREE.WebGLRenderer({
antialias: true,
powerPreference: 'high-performance'
})
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
// 光照配置
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
directionalLight.position.set(100, 200, 100)
}
}关于 setPixelRatio 的设置:在高 DPI 屏幕上,我们将其限制在 2 以内,在视觉质量和性能之间取得平衡。
区块网格生成是渲染系统中最复杂也最关键的部分。一个 16×16×16 的区块最多包含 4096 个方块,WebCraft 采用了多种优化策略:
面剔除(Face Culling):只渲染暴露在空气中的方块面。如果一个方块的某一面被另一个实心方块遮挡,那么这个面就不需要渲染。这个优化可以将需要渲染的三角形数量减少 80% 以上。
private shouldRenderFace(x: number, y: number, z: number,
dx: number, dy: number, dz: number): boolean {
const neighbor = this.getBlockAt(x + dx, y + dy, z + dz)
// 如果相邻方块是空气或透明方块,需要渲染这个面
if (neighbor === BlockType.AIR) return true
if (isTransparent(neighbor)) return true
return false
}三层渲染顺序:为了正确处理透明物体:
- 不透明层(renderOrder = 0):草地、石头等实心方块
- 半透明层(renderOrder = 1):树叶、玻璃等
- 水面层(renderOrder = 2):水面最后渲染以正确混合
纹理图集(Texture Atlas):所有方块的纹理被打包到一张大图中,通过 UV 坐标选择不同纹理区域,避免频繁切换纹理。
花草等植物方块采用"X 形交叉面"的渲染方式:
private addCrossPlanes(x: number, y: number, z: number): void {
// 创建两个对角交叉的平面
// 第一个对角面:从 (x, y, z) 到 (x+1, y+1, z+1)
// 第二个对角面:从 (x+1, y, z) 到 (x, y+1, z+1)
}这种渲染方式使得植物从任何角度看都有立体感,同时只需要 4 个三角形。
WebCraft 支持第一人称和第三人称两种视角,通过 V 键切换。第三人称视角有一个重要细节——相机碰撞检测:
private updateThirdPersonCamera(): void {
// 计算理想的相机位置(玩家身后 5 米)
const idealPosition = this.player.position.clone()
.add(new THREE.Vector3(0, 2, -5).applyQuaternion(this.player.quaternion))
// 射线检测:从玩家到理想位置之间是否有障碍物
const raycaster = new THREE.Raycaster(this.player.position, direction)
const intersects = raycaster.intersectObjects(this.world.getCollidables())
if (intersects.length > 0 && intersects[0].distance < 5) {
// 有障碍物,将相机拉近
this.camera.position.copy(this.player.position)
.add(direction.multiplyScalar(intersects[0].distance - 0.5))
}
}WebCraft 的天空使用自定义 GLSL 着色器,根据片元在世界空间中的高度,在顶部颜色和底部颜色之间进行插值,创造出自然的天空渐变效果。天空系统还包括随时间移动的太阳、月亮,以及夜晚可见的星空粒子系统。
WebCraft 实现了一个简化但有效的物理系统:
export class PhysicsSystem {
private readonly GRAVITY = 20 // 重力加速度 (m/s²)
private readonly WATER_GRAVITY = 8 // 水下重力
private readonly JUMP_VELOCITY = 8 // 跳跃初速度 (m/s)
private readonly TERMINAL_VELOCITY = 50 // 终端速度 (m/s)
update(body: IPhysicsBody, world: ICollisionWorld, deltaTime: number): void {
// 应用重力
const gravity = body.isInWater ? this.WATER_GRAVITY : this.GRAVITY
body.velocity.y -= gravity * deltaTime
body.velocity.y = Math.max(body.velocity.y, -this.TERMINAL_VELOCITY)
// 水中阻力
if (body.isInWater) {
body.velocity.x *= 0.8
body.velocity.z *= 0.8
}
// 分轴碰撞检测与解析
this.resolveCollisions(body, world, deltaX, deltaY, deltaZ)
}
}AABB(Axis-Aligned Bounding Box,轴对齐包围盒)是游戏开发中最常用的碰撞检测方法:
export function aabbIntersects(a: AABB, b: AABB): boolean {
return (
a.minX < b.maxX && a.maxX > b.minX &&
a.minY < b.maxY && a.maxY > b.minY &&
a.minZ < b.maxZ && a.maxZ > b.minZ
)
}碰撞检测按 Y → X → Z 的顺序解析,先处理垂直方向,确保玩家能正确站在地面上。每个轴独立处理,如果发生碰撞,将物体推到方块表面并清零该轴速度。
当玩家进入水中时,物理行为发生明显变化:
- 降低重力:从 20 m/s² 降到 8 m/s²,模拟浮力
- 增加阻力:水平移动速度衰减
- 游泳控制:空格键上浮,Shift 键下潜
- 视觉效果:蓝色滤镜覆盖
Minecraft 类游戏最吸引人的特性之一就是"无限世界"。程序化生成的核心思想是:使用数学函数根据位置坐标计算出地形数据。只要函数是确定性的(相同输入产生相同输出),就能保证世界的一致性。
WebCraft 使用 Simplex 噪声生成地形。分形噪声(fBm)通过叠加不同频率和振幅的噪声来创造更自然的地形:
fractalNoise2D(x: number, y: number, octaves: number = 4): number {
let total = 0, amplitude = 1, frequency = 1, maxValue = 0
for (let i = 0; i < octaves; i++) {
total += this.noise2D(x * frequency, y * frequency) * amplitude
maxValue += amplitude
amplitude *= 0.5 // persistence
frequency *= 2 // lacunarity
}
return total / maxValue
}低频噪声决定大的地形起伏(山脉、平原),高频噪声添加细节(小丘、凹坑)。
export class TerrainGenerator {
private readonly BASE_HEIGHT = 64
private readonly WATER_LEVEL = 62
private getBlockAt(worldX: number, worldY: number, worldZ: number,
surfaceHeight: number, biome: BiomeType): BlockType {
// 地表以上
if (worldY > surfaceHeight) {
return worldY <= this.WATER_LEVEL ? BlockType.WATER : BlockType.AIR
}
// 检测洞穴(3D 噪声)
if (this.isCave(worldX, worldY, worldZ)) return BlockType.AIR
// 地表层
if (worldY === surfaceHeight) return this.getSurfaceBlock(biome)
// 次表层
if (worldY >= surfaceHeight - 3) return this.getSubsurfaceBlock(biome)
// 深层
return BlockType.STONE
}
}WebCraft 实现了五种生物群系:平原、湖泊、山地、沙漠、雪地。生物群系由两个噪声维度(湿度和温度)决定:
getBiomeAt(worldX: number, worldZ: number): BiomeType {
// 出生点保护区强制为平原
if (Math.sqrt(worldX * worldX + worldZ * worldZ) < 100) {
return BiomeType.PLAINS
}
const moisture = this.noise.noise2D(worldX * 0.005, worldZ * 0.005)
const temperature = this.noise.noise2D(worldX * 0.005 + 1000, worldZ * 0.005 + 1000)
if (moisture > 0.3) return BiomeType.LAKE
if (temperature < -0.2) return BiomeType.MOUNTAIN
if (temperature > 0.4 && moisture < -0.2) return BiomeType.DESERT
return BiomeType.PLAINS
}WebCraft 生成了四个标志性建筑:
- 罗马斗兽场:位于出生点,椭圆形设计,四层拱门结构
- 金字塔:位于东侧,可探索的内部迷宫结构
- 故宫:位于西南侧,中式宫殿建筑群
- 城堡:位于西北侧,欧式城堡风格
每个地标都有独立的生成器,根据世界坐标返回对应位置的方块类型。
WebCraft 实现了 9 种动物:7 种陆地动物(牛、猪、羊、鸡、兔子、狼、狐狸)和 2 种水生鱼类。
export abstract class Animal {
protected state: 'idle' | 'wandering' | 'fleeing' = 'idle'
protected updateAI(deltaTime: number, playerPosition: THREE.Vector3): void {
switch (this.state) {
case 'idle':
// 站立不动,偶尔转头
break
case 'wandering':
// 向随机目标位置移动
this.moveTowards(this.targetPosition, deltaTime)
break
case 'fleeing':
// 远离玩家
const awayFromPlayer = this.position.clone().sub(playerPosition).normalize()
this.moveTowards(this.position.clone().add(awayFromPlayer.multiplyScalar(10)), deltaTime)
break
}
}
}动物由 EntityManager 统一管理,按区块分组,只有玩家附近的区块中的动物才会被更新。
一个游戏日等于 24000 ticks,约 20 分钟现实时间:
export class TimeSystem {
getTimeOfDay(): 'dawn' | 'day' | 'dusk' | 'night' { ... }
getLightLevel(): number {
// 返回 0.2(夜间)到 1.0(白天)的光照强度
}
getSunAngle(): number {
return (this.time / 24000) * Math.PI * 2
}
}天气系统支持晴天和雨天,5-10 分钟随机切换,30 秒平滑过渡。雨效果使用粒子系统实现。
WebCraft 的音频系统完全基于 Web Audio API,支持背景音乐、音效和 3D 空间音效:
export class AudioManager {
private context: AudioContext
private masterGain: GainNode
private musicGain: GainNode
private sfxGain: GainNode
// 播放 3D 空间音效
play3DSFX(buffer: AudioBuffer, position: THREE.Vector3,
listenerPosition: THREE.Vector3): void {
const panner = this.context.createPanner()
panner.panningModel = 'HRTF'
panner.distanceModel = 'inverse'
panner.refDistance = 1
panner.maxDistance = 50
// 设置位置...
}
}WebCraft 的一个特色是程序化音频合成。脚步声、动物叫声等音效在运行时使用 Web Audio API 合成:
synthesizeFootstep(material: 'grass' | 'stone' | 'sand' | 'wood'): AudioBuffer {
const buffer = this.context.createBuffer(1, sampleRate * 0.1, sampleRate)
const data = buffer.getChannelData(0)
for (let i = 0; i < data.length; i++) {
let sample = (Math.random() - 0.5) * 2 // 基础噪声
switch (material) {
case 'grass': sample *= Math.exp(-t * 30) * 0.3; break
case 'stone': sample *= Math.exp(-t * 50) * 0.5; break
// ...
}
data[i] = sample
}
return buffer
}程序化音频的优势:减小文件体积、无限变化、动态适应。
WebCraft 使用 IndexedDB 实现存档系统,支持 5 个手动槽位和 1 个自动存档:
export class SaveManager {
async saveGame(slot: number, world: World, player: Player): Promise<void> {
const saveData: SaveData = {
slot,
timestamp: Date.now(),
worldSeed: world.getSeed(),
playerPosition: { x, y, z },
playerRotation: { x, y },
timeOfDay: world.getTimeSystem().getTime(),
weather: world.getWeatherSystem().getCurrentWeather()
}
// 保存主存档数据
await saveStore.put(saveData)
// 保存已修改的区块
for (const chunk of world.getModifiedChunks()) {
await chunkStore.put({
id: `${slot}_${chunk.x}_${chunk.y}_${chunk.z}`,
blocks: Array.from(chunk.getBlocks())
})
}
}
}自动存档功能每 5 分钟在后台保存游戏进度,防止意外丢失。
- 视距控制:只加载玩家视距范围内的区块(默认 8 个区块 = 128 米)
- 优先级队列:距离玩家更近的区块优先加载
- 异步加载:区块生成在后台进行,不阻塞主线程
- 视锥剔除:只渲染在相机视锥内的区块
- 空区块跳过:完全由空气组成的区块不渲染
- 网格缓存:区块网格只在方块变化时重建
- 类型化数组:使用
Uint8Array存储方块数据,每个方块仅 1 字节 - 对象池:复用频繁创建的对象,减少 GC 压力
- 缓存策略:生物群系查询结果缓存
WebCraft 采用规格驱动开发。每个功能在开发前都会编写详细的规格文档,包括用户故事、验收标准、功能需求和成功指标。项目共有 18 个完整的功能规格文档。
每个功能都是独立的模块,有清晰的接口定义,可以独立开发和测试。
WebCraft 展示了 Web 技术在游戏开发领域的强大能力:
- 无限世界:基于程序化生成的无限可探索世界
- 完整的物理系统:重力、碰撞、水下物理
- 丰富的生态系统:9 种动物、多种植物、昼夜天气
- 沉浸式音频:背景音乐、3D 音效、程序化合成
- 持久化存档:5 个手动槽位 + 自动存档
- 纯前端实现:不依赖任何后端服务
- 程序化生成:噪声函数驱动的无限世界
- 高效渲染:面剔除、视锥剔除、区块管理
- 程序化音频:运行时合成音效
- 规格驱动开发:18 个完整的功能规格
- 多人游戏:WebSocket/WebRTC 联机
- 更多方块类型:红石系统、装饰方块
- 生物行为:驯服、繁殖
- 地形特性:更多生物群系、地下结构
- 性能优化:Web Worker、WebGPU
WebCraft 项目证明了:浏览器已经不再只是展示网页的工具,它是一个功能完整的应用平台。通过现代 Web API(WebGL、Web Audio、IndexedDB),我们可以构建出媲美原生应用的复杂游戏。
这个项目也是对"简单"的一次致敬。Minecraft 的成功告诉我们,游戏的魅力不在于技术的复杂程度,而在于它能否激发玩家的创造力。WebCraft 虽然在技术上做了大量工作,但最终目标始终是:给玩家一个可以自由创造的世界。
| 按键 | 功能 |
|---|---|
| WASD / 方向键 | 移动 |
| 鼠标 | 视角控制 |
| 空格 | 跳跃 / 水中上浮 |
| Shift | 冲刺 / 水中下潜 |
| 左键 | 破坏方块 |
| 右键 | 放置方块 |
| 按键 | 功能 |
|---|---|
| 1-9, 0 | 选择方块类型 |
| Tab | 循环切换方块 |
| M | 打开/关闭大地图 |
| V | 切换第一/三人称视角 |
| C | 角色选择 |
| ESC | 存档菜单 |
本文约 10000 字,详细介绍了 WebCraft 项目的技术架构、核心系统和开发实践。