主题切换
3D 游戏开发基础
预计阅读 25 分钟
版本信息
最后更新:2026-07-09 · 适用基础库:≥ 3.9.0 · 微信开发者工具:稳定版 1.06+
微信小游戏虽然以 2D 休闲游戏为主,但 Unity 和 Cocos Creator 3.x 都支持 3D 渲染。理解 3D 基础原理有助于在需要时做出正确的技术决策。本章以前端视角切入,帮助你建立 3D 核心概念体系。
前端 3D 经验 vs 游戏 3D
前端对比:CSS 3D vs 游戏 3D
| 维度 | 前端 CSS 3D / Three.js | 游戏引擎 3D |
|---|---|---|
| 坐标系 | CSS: 屏幕左上角,Z 轴向屏幕内 | 游戏: 通常 Y-up,Z 指向屏幕外 |
| 变换 | transform: rotate/translate/scale | 模型矩阵(Model Matrix) |
| 投影 | CSS perspective: 800px | 透视投影矩阵 |
| 渲染管线 | GPU 自动合成(Composite Layer) | 顶点着色器 → 光栅化 → 片段着色器 |
| 光照 | CSS lighting-color 有限支持 | Phong / PBR / 全局光照 |
关键差异: 前端 3D 本质上是"CSS 为 2D 元素添加 3D 视觉效果";游戏 3D 需要对整个渲染管线有底层理解。如果你用过 Three.js,你会对大部分概念感到熟悉——Three.js 本质上是对 WebGL 的封装,与游戏引擎的 3D 渲染是同一套底层原理。
3D 数学基础速览
向量 (Vector)
向量在游戏中最常用于表示位置、方向、速度、法线。
typescript
// 3D 向量基本运算
class Vec3 {
constructor(
public x: number,
public y: number,
public z: number
) {}
// 加法:位置偏移
add(v: Vec3): Vec3 {
return new Vec3(this.x + v.x, this.y + v.y, this.z + v.z)
}
// 缩放:改变速度大小
scale(s: number): Vec3 {
return new Vec3(this.x * s, this.y * s, this.z * s)
}
// 点积:判断朝向、计算夹角
dot(v: Vec3): number {
return this.x * v.x + this.y * v.y + this.z * v.z
}
// 叉积:计算法线、垂直方向
cross(v: Vec3): Vec3 {
return new Vec3(
this.y * v.z - this.z * v.y,
this.z * v.x - this.x * v.z,
this.x * v.y - this.y * v.x
)
}
// 长度
get length(): number {
return Math.sqrt(this.x ** 2 + this.y ** 2 + this.z ** 2)
}
// 归一化(单位向量,只保留方向)
normalize(): Vec3 {
const len = this.length
return len > 0 ? this.scale(1 / len) : new Vec3(0, 0, 0)
}
}
// 实战应用
const playerPos = new Vec3(0, 0, 0)
const enemyPos = new Vec3(10, 5, 0)
// 方向向量(从玩家指向敌人)
const direction = new Vec3(
enemyPos.x - playerPos.x,
enemyPos.y - playerPos.y,
enemyPos.z - playerPos.z
).normalize()
// 判断敌人是否在玩家前方(点积 > 0 表示在前方)
const playerForward = new Vec3(0, 0, 1) // 玩家面朝 Z+ 方向
const isInFront = playerForward.dot(direction) > 0矩阵 (Matrix)
矩阵用于变换:位移(Translate)、旋转(Rotate)、缩放(Scale)。在 WebGL/OpenGL 中使用 4×4 矩阵统一表示所有变换。
┌ ┐
│ 1 0 0 tx │ 列主序中 tx/ty/tz 是位移分量
│ 0 1 0 ty │
│ 0 0 1 tz │
│ 0 0 0 1 │
└ ┘前端类比:
transform: translate(100px, 0)≈ 位移矩阵transform: rotate(45deg)≈ 旋转矩阵transform: scale(2)≈ 缩放矩阵transform: matrix(a, b, c, d, tx, ty)≈ 2D 仿射矩阵(3D 的简写版)
四元数 (Quaternion)
四元数是旋转的另一种表示方式,避免万向节死锁 (Gimbal Lock)。你不需要理解四元数的数学推导,只需知道:
typescript
// 欧拉角:直观但可能万向节死锁
// node.eulerAngles = new Vec3(0, 90, 0) // 绕 Y 轴旋转 90°
// 四元数:数学稳定但不够直观
// node.rotation = Quaternion.fromEuler(0, 90, 0)
// 四元数插值(Slerp)实现平滑旋转过渡
// node.rotation = Quaternion.slerp(from, to, 0.5) // 两个旋转间插值 50%3D 渲染管线
从前端 DOM → Layout → Paint → Composite 到游戏 3D 的 Vertices → Shaders → Rasterize → Pixels:
┌─────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 顶点着色器 │ → │ 图元装配 │ → │ 光栅化 │ → │ 片段着色器 │
│ (Vertex) │ │ (Primitive) │ │ (Rasterize) │ │ (Fragment) │
│ 坐标变换 │ │ 点/线/三角面 │ │ 生成像素片段 │ │ 逐像素着色 │
└─────────────┘ └──────────────┘ └──────────────┘ └──────────────┘glsl
// 顶点着色器 — 运行在每个顶点上
// 将模型坐标变换到屏幕坐标
attribute vec4 a_position;
uniform mat4 u_modelViewProjection;
void main() {
gl_Position = u_modelViewProjection * a_position;
}
// 片段着色器 — 运行在每个像素上
// 计算最终颜色
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}光照模型
Phong 光照模型(经典)
最终颜色 = 环境光 + 漫反射 + 镜面反射
Ambient + Diffuse + Specular| 分量 | 含义 | 前端类比 |
|---|---|---|
| 环境光 | 场景基础亮度,无方向 | body { background: #eee } |
| 漫反射 | 光线直射面更亮 | 无直接前端类比 |
| 镜面反射 | 高光/反光 | box-shadow 的部分效果 |
PBR(基于物理的渲染)
现代引擎(Unity、Cocos Creator 3.x)使用 PBR,核心参数:
- Albedo(反照率):物体基础颜色
- Metallic(金属度):0 = 非金属,1 = 金属
- Roughness(粗糙度):0 = 镜面,1 = 完全漫反射
微信小游戏 3D 能力现状
现实限制
- WebGL 1.0/2.0:微信小游戏不支持 WebGPU,仅支持 WebGL。WebGL 2.0 需要基础库 ≥ 2.15.0
- iOS 高性能模式:支持 WebGL 2.0,性能接近原生 Metal(因为微信底层将 WebGL 转译为 Metal)
- 性能预算:顶点数 ≤ 50K,Draw Call ≤ 100,纹理总内存 ≤ 50MB(休闲游戏基准)
- 不支持的功能:Compute Shader、Geometry Shader、Tessellation
- 推荐引擎:Unity(WX-WASM-SDK 导出 WebGL)或 Cocos Creator 3.8+
技术选型建议
| 游戏类型 | 推荐方案 | 原因 |
|---|---|---|
| 休闲 2D | Cocos Creator 3.8.x LTS 2D 模式 | 轻量、热更新友好;2.4.x 仅维护旧项目,不再推荐 |
| 3D 轻度(AR 拍照、3D 展示) | Cocos Creator 3.8.x | 包体小(~2MB 引擎) |
| 3D 中度(跑酷、射击) | Unity 2022.3 LTS / Unity 6.3 LTS(6000.3.x)+ WX-WASM-SDK | 生态完善、工具链成熟 |
| 3D 重度(开放世界) | 不建议微信小游戏 | 4MB 主包限制 + WebGL 性能瓶颈 |
骨骼动画原理
骨骼动画的核心思想:网格顶点随骨骼移动,而非存储每一帧的顶点位置。
骨架 (Skeleton) 蒙皮网格 (Skinned Mesh)
│ │
├── 根骨骼 (Root) ├── 顶点位置 (静止姿态)
├── 脊椎 (Spine) ├── 骨骼权重 (每顶点 4 根骨骼)
├── 手臂 (Arm L/R) └── 骨骼索引
└── 腿 (Leg L/R)关键概念:
- 绑定姿态 (Bind Pose):网格刚绑定到骨架时的姿态,作为基准
- 蒙皮 (Skinning):根据骨骼当前变换,加权计算每个顶点的最终位置
- 顶点权重:每个顶点最多受 4 根骨骼影响,权重总和 = 1.0
在代码层面你通常不需要手动处理这些——Cocos Creator 和 Unity 都有完整的骨骼动画系统,直接导入 Spines/FBX 即可。
📚 相关阅读
- 05 引擎对比与选型 — 何时选择 3D 引擎
- 06 引擎深度实战 — Cocos Creator 3.x 和 Unity 实战
- 08 性能优化与包体控制 — WebGL Shader 优化专题
- iOS 高性能模式 — iOS 下的 3D 性能表现
📝 课后练习
练习 1:3D 向量运算实现追逐 AI
题目: 使用向量运算实现一个简单的追逐 AI:敌人始终面向玩家并以固定速度移动。
参考答案:
typescript
class ChaseAI {
position = new Vec3(0, 0, 0)
speed = 5 // 单位/秒
update(dt: number, playerPos: Vec3): void {
// 计算方向向量
const direction = new Vec3(
playerPos.x - this.position.x,
playerPos.y - this.position.y,
playerPos.z - this.position.z
)
const distance = direction.length
// 距离太近时停止(避免抖搂)
if (distance < 0.5) return
// 归一化方向并乘以速度和时间
const velocity = direction.normalize().scale(this.speed * dt)
this.position = this.position.add(velocity)
}
}练习 2:评估你的游戏是否需要 3D
题目: 假设你要开发一款「合成大西瓜」类型的物理游戏,请分析选择 3D 还是 2D 的技术考量。
参考答案:
| 考量维度 | 2D (Cocos 2.4) | 3D (Cocos 3.8 / Unity) |
|---|---|---|
| 物理引擎 | Box2D(成熟稳定) | PhysX / Bullet(3D 物理) |
| 渲染性能 | 极低 GPU 占用 | 即使做 2D 也需要 3D 管线开销 |
| 包体大小 | ~1.5MB | ~3-5MB |
| 视觉效果 | 纯 2D 贴图 | 可用 Shader 做光影特效 |
| 开发成本 | 低(素材制作快) | 高(需要 3D 基础) |
| 适配难度 | 低(所有机型友好) | 中(低端机可能掉帧) |
结论: 对于「合成大西瓜」这类游戏,2D 是更合理的选择。3D 的额外开销无法带来对应的体验提升。只有当游戏需要:
- 3D 视角的沉浸感(如跑酷、射击)
- 复杂的 3D 碰撞/物理
- 动态光照和阴影效果
时才值得引入 3D 方案。