人人都會寫的3D小遊戲

image

近期在嘗試着學習寫一些3D小遊戲,分享一下成果。
嚴格上說還算不上完整的遊戲,感興趣的朋友能夠觀摩一下。

準備

在這裏用到的惟一素材:小飛機

是在 Blender 軟件了裏面簡單設計的
git

Blender 是一個免費開源的建模軟件

若是感興趣,這裏推薦B站上的一個 入門教程github

下面是個人一些步驟

cover-0.png

  1. 使用簡單的幾何元素構建出飛機模型
  2. 製做2個小動畫:螺旋槳旋轉機身搖晃
  3. 導出 .glb 格式模型

模型導入到 Babylon 的代碼以下

主要用到2個參數 meshesanimationGroups框架

{
    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

代碼

在這個項目主要實現一下幾個環節dom

  1. 基本場景的搭建
  2. 飛機跟隨鼠標的運動
  3. 粒子的生成和運動
  4. 飛機與粒子的碰撞

飛機模型的加載

// 加載飛機模型
    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()
            }
        }
    }

飛機模型的運動跟隨

  1. 鼠標移動時獲取 鼠標座標飛機 座標
  2. 飛機座標 須要 3維座標屏幕座標 的轉化
  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')
            }
        }
    })
}

其餘

項目的主要流程就是這些了

還有其餘的一些,好比:async

  • 場景的搭建
  • 相機的處理
  • 燈光的處理
  • 遊戲狀態的處理

歡迎到GitHub查看完整代碼ide

謝謝閱讀

喜歡的話,點贊 star 支持
相關文章
相關標籤/搜索