1. 程式人生 > >微信小遊戲開發之三:實現小遊戲的簡易引擎

微信小遊戲開發之三:實現小遊戲的簡易引擎

一、建立引擎目錄

在主目錄下建立名為'lib'的資料夾,存放引擎程式碼

二、建立所有遊戲元素的基類:Node

在'lib'資料夾下建立'node.js'檔案;

一個元素,需要座標去定義位置,長寬來定義範圍,還需要能夠切換顯示狀態,新增子元素和獲取父元素等等

程式碼如下:

export default class Node {
    constructor(x = 0, y = 0, width = 0, height = 0) {
        // 在父元素中的座標
        this.x = x
        this.y = y
        // 父元素在世界座標系中的座標
        this.absX = 0
        this.absY = 0
        // 錨點
        this.anchorPointX = 0.5
        this.anchorPointY = 0.5
        // 寬和高
        this.width = width
        this.height = height
        // 子元素
        this.children = []
        // 是否顯示
        this.visible = true
    }
    /**
     *  將當前元素繪製到螢幕中
     *  @ctx    canvas_context
     */
    draw(ctx) {
        if (this.visible) {
            this.children.forEach((child) => {
                child.draw(ctx)
            })
        }
        // node本身不進行繪製,需要讓子類自行繪製
    }

    /**
     *  增加一個子元素
     *  @child  子元素
     */
    addChild(child) {
        child.parent = this
        child.absX += this.x
        child.y += this.y
        this.children.push(child)
        console.log("node.addChild")
    }

    /**
     *  獲得計算錨點後的全域性座標
     */
    getFixedPosition() {
        let absPos = this.getAbsPosition()
        return {
            x: absPos.x - this.anchorPointX * this.width,
            y: absPos.y - this.anchorPointY * this.height
        }
    }
    /**
     *  設定錨點,值範圍在(0, 1)
     */
    setAnchorPoint(x, y) {
        if (x < 0) x = 0
        if (y < 0) y = 0
        if (x > 1) x = 1
        if (y > 1) y = 1
        this.anchorPointX = x
        this.anchorPointY = y
    }




    /**
     *  快速設定寬和高
     */
    setContentSize(width, height) {
        this.width = width
        this.height = height
    }

    /**
     *  快速設定座標,並修改子元素的絕對座標
     */
    setPosition(x, y) {
        this.x = x
        this.y = y
        this.setChildrenAbsPosition(this.getAbsPosition())
    }
    setPositionX(x) {
        this.x = x
        this.setChildrenAbsPosition(this.getAbsPosition())
    }
    setPositionY(y) {
        this.y = y
        this.setChildrenAbsPosition(this.getAbsPosition())
    }

    /**
     *  獲得未計算錨點的全域性座標
     */
    getAbsPosition() {
        return {
            x: this.x + this.absX,
            y: this.y + this.absY
        }
    }

    /**
     *  修改所有子元素的座標
     */
    setChildrenAbsPosition(absPos) {
        this.children.forEach((child) => {
            child.setAbsPosition(absPos)
        })
    }

    setAbsPosition(absPos) {
        this.absX = absPos.x
        this.absY = absPos.y
        this.setChildrenAbsPosition(absPos)
    }
}



三、實現Label類

在'lib'資料夾下新建'label.js'檔案 由於計算文字寬度,必須要獲得canvas的context, 所以在'lib'資料夾下新建'warehouse'類作為資料倉庫,儲存遊戲中需要用到的常量
let ctx

function WareHouse() {
    this.getCtx = function () {
        if (!ctx) {
            ctx = canvas.getContext('2d')
        }
        return ctx
    }
}

export default (new WareHouse())

‘lib/label.js’
import Node from './node.js'
import WareHouse from './warehouse.js'

export default class Lable extends Node {
    constructor(text = '', x = 0, y = 0, fontSize = "20px", fontFamily = "Courier New") {
        super(x, y)
        this.fontSize = fontSize
        this.fontFamily = fontFamily
        this.text = text
    }

    draw(ctx) {
        super.draw(ctx)
        if (this.visible) {
            ctx.font = this.fontSize + " " + this.fontFamily
            let fixedPos = this.getFixedPosition()
            ctx.fillText(this.text, fixedPos.x, fixedPos.y)
        }
    }

    /**
     *  Label的寬度需要通過canvas進行測量,所以要特殊處理
     */
    getFixedPosition() {
        let ctx = WareHouse.getCtx()
        this.width = ctx.measureText(this.text).width
        return super.getFixedPosition()
    }

    setFontSize(fontsize) {
        this.fontSize = fontSize
    }
    setFontFamily(fontFamily) {
        this.fontFamily = fontFamily
    }
}


四、實現Sprite類

在'lib'資料夾下新建'sprite.js'檔案
import Node from './node'

export default class Sprite extends Node {
    constructor(imgSrc = '', width = 0, height = 0, x = 0, y = 0) {
        super(x, y, width, height)
        this.img = new Image()
        this.img.src = imgSrc
    }

    draw(ctx) {
        let absPos = this.getFixedPosition()
        if (this.visible) {
            ctx.drawImage(
                this.img,
                absPos.x,
                absPos.y,
                this.width,
                this.height
            )
        }
        super.draw(ctx)
    }

    setImage(imgSrc){
        this.img.src = imgSrc
    }
}


五、檢視效果

在'main.js'中編輯如下程式碼:
import './js/libs/weapp-adapter'
import './js/libs/symbol'

import Sprite from './lib/sprite.js'
import Label from './lib/label.js'
import WareHouse from './lib/warehouse.js'

// 獲取canvas上下文
let ctx = WareHouse.getCtx()

//新建一個Sprite和一個Label
let helloSprite = new Sprite("hello.png")
let helloLable = new Label("hello world")
let worldLable = new Label("world", 0, 50)

helloSprite.addChild(helloLable)
helloLable.addChild(worldLable)


helloSprite.setPosition(150, 50)
helloSprite.setContentSize(50, 50)
helloSprite.setAnchorPoint(0.5,0.5)
helloLable.setAnchorPoint(0.5, 0.5)

// 模擬遊戲迴圈
window.requestAnimationFrame(loop, canvas)

// update中渲染元素
function update() {
    //helloSprite.setPositionY(helloSprite.y + 0.1)
    helloSprite.draw(ctx)
}

function loop() {
    // 清空顯示區域
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    // 渲染元素
    update()
    // 持續迴圈呼叫自身
    window.requestAnimationFrame(loop, canvas)
}


可以看到如下效果:
未完待續...