近期在尝试着学习写一些3D小游戏,分享一下成果。
严格上说还算不上完整的游戏,感兴趣的朋友可以观摩一下。
- 预览地址:https://luosijie.github.io/ga...
- git仓库:https://github.com/luosijie/g...
- 语言:TypeScript
- 框架:Babylon
准备
在这里用到的唯一素材:小飞机
是在 Blender 软件了里面简单设计的
Blender 是一个免费开源的建模软件
如果感兴趣,这里推荐B站上的一个 入门教程
下面是我的一些步骤
- 使用简单的几何元素构建出飞机模型
- 制作2个小动画:螺旋桨旋转 和 机身摇晃
- 导出 .glb 格式模型
模型导入到 Babylon 的代码如下
主要用到2个参数 meshes 和 animationGroups
{
animationGroups: (2) [AnimationGroup, AnimationGroup]
geometries: (9) [Geometry, Geometry, Geometry, Geometry, Geometry]
lights: []
meshes: (10) [Mesh, Mesh, Mesh, Mesh, Mesh, Mesh, Mesh, Mesh, Mesh, Mesh]
particleSystems: []
skeletons: []
transformNodes: (5) [TransformNode, TransformNode, TransformNode]
__proto__: Object
代码
在这个项目主要实现一下几个环节
- 基本场景的搭建
- 飞机跟随鼠标的运动
- 粒子的生成和运动
- 飞机与粒子的碰撞
飞机模型的加载
// 加载飞机模型
private async loadPlane(): Promise<any> {
// 新建一个透明元素 包裹模型
const container = MeshBuilder.CreateBox('plane-container', { width: 1, depth: 2, height:1 }, this.scene)
container.isVisible = false;
// 调整到与模型重合的位置
container.bakeTransformIntoVertices(Matrix.Translation(0, 1.2, 0.8))
container.rotation.y = -Math.PI / 2
container.position.x = 0.6
// 加载飞机模型
const glb = await SceneLoader.ImportMeshAsync(null, './public/', 'plane.glb', this.scene)
const root = glb.meshes[0]
console.log('glb', glb)
// 绑定父子关系
root.parent = container
return {
mesh: container,
fly: () => {
glb.animationGroups[0].play(true)
glb.animationGroups[1].play(true)
},
stop: () => {
glb.animationGroups[0].stop(),
glb.animationGroups[1].stop()
}
}
}
飞机模型的运动跟随
- 鼠标移动时获取 鼠标坐标 和 飞机 坐标
- 飞机坐标 需要 3维坐标 到 屏幕坐标 的转化
- 让 飞机坐标 往 鼠标坐标 移动
...
// 获取鼠标坐标存储
this.scene.onPointerObservable.add(info => {
const { event } = info
// 存储鼠标坐标数据
if (event.type === 'pointermove') {
this.pointerPos = {
x: event.x,
y: event.y
}
}
})
...
/** Loop中更新plane坐标 */
private updatePlane(): void {
// 设置平滑系数-不断尝试得到到数值
const smoothing = 2000
// 获取plane屏幕坐标
const originPos = this.WorldToScreen(this.plane.mesh.position)
if (this.pointerPos.x && this.pointerPos.y) {
// 计算鼠标位置 和 plane 位置得距离
const deltaX = (this.pointerPos.x - originPos.x) / smoothing
const deltaY = (this.pointerPos.y - originPos.y) / smoothing
// plane 朝鼠标的方向移动
this.plane.mesh.position.x += deltaX
this.plane.mesh.position.y -= deltaY
}
}
生成粒子
/**
* 生成粒子数据
* 每个间隔时间生成粒子:并插入到 particles 中
*/
private initParticles(): void {
// 限制 scene 最多的粒子为90
const LIMIT = 90
this.particles = []
setInterval(() => {
if (this.particles.length > LIMIT || this.state !== State.GAME) return
// 创建粒子
const particle = this.createParticle()
// 随机放置粒子
particle.position.x = 20 + Math.random() * 20
particle.position.y = -10 + Math.random() * 20
particle.position.z = 0
// 粒子插入 particles 中:方便后面更新操作
this.particles.push(particle)
}, 300)
}
更新粒子运动 并检测 是否与飞机碰撞
/** Loop中更新粒子数据 */
private updateParticles(): void {
// 定义粒子移动速度
const SPEED = 0.15
this.particles.forEach((e, index) => {
// 粒子差不多离开屏幕后,回收重制到初始位置
if (e.position.x < -20) {
e.position.x = 20 + Math.random() * 20
}
e.position.x -= SPEED
// 检测粒子是否和 plane 发生碰撞
const collided = e.intersectsMesh(this.plane.mesh)
if (collided) {
this.particles.splice(index, 1)
e.dispose()
if (e.name === 'sphere') {
this.score++
this.updateScore()
console.log('collided')
}
}
})
}
其他
项目的主要流程就是这些了
还有其他的一些,比如:
- 场景的搭建
- 相机的处理
- 灯光的处理
- 游戏状态的处理
欢迎到GitHub查看完整代码
谢谢阅读
喜欢的话,点赞 star 支持
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。