原生js打砖块游戏(一)——基础框架

前言

    最近看到了萧井陌的几个游戏的源码,好奇也跟着打了打,真的是很有收获。

    最后会实现一个打方块的游戏,完全由原生js编写,当然,用了canvas。接下来会连续发几篇文章一步步建立并完善我的小游戏,并记录萧大比较酷的观点以及我自己的一些想法。

游戏效果

    只是一个比较基础的版本,只有球和挡板,障碍物和界面效果记录数据什么的都没有加,以后会逐渐完善。


完整代码

    先给出完整的代码吧,因为需要说明的地方不太多,而且代码量也很小。

    图片的话自己随手截的,如果想测试的话请自行修改相关的参数。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>game 1</title>
    </head>
    <style>
        canvas {
            border: 1px solid black;
        }
    </style>
    <body>

    <canvas id="id-canvas" width="1000" height="900"></canvas>

<script>

var log = console.log.bind(console)

var imageFromPath = function(path) {
    var img = new Image()
    img.src = path
    return img
}

var Paddle = function() {
    var image = imageFromPath('paddle.png')
    var o = {
        image: image,
        x: 400,
        y: 750,
        speed: 25,
    }

    o.moveLeft = function() {
        this.x -= this.speed
    }

    o.moveRight = function() {
        this.x += this.speed
    }

    o.collide = function(ball) {
        if (ball.y + ball.image.height > o.y) {
            if (ball.x > o.x && ball.x < o.x + o.image.width) {
                return true
            }
        }
        return false
    }

    return o
}

var Ball = function() {
    var image = imageFromPath('ball.png')
    var o = {
        image: image,
        x: 200,
        y: 400,
        speedX: 15,
        speedY: 15,
        fired: false,
    }

    o.move = function () {
        if (o.fired) {
            if (o.x < 0 || o.x > 1000) {
                o.speedX = -o.speedX
            }
            if (o.y < 0 || o.y > 900) {
                o.speedY = -o.speedY
            }

            // move
            o.x += o.speedX
            o.y += o.speedY
        }
    }

    o.fire = function() {
        o.fired = true
    }

    return o
}


var GuaGame = function() {
    var g = {
        actions: {},
        keydowns: {},
    }
    var canvas = document.querySelector('#id-canvas')
    var context = canvas.getContext('2d')
    g.canvas = canvas
    g.context = context

    // draw
    g.drawImage = function(guaImage) {
        this.context.drawImage(guaImage.image, guaImage.x, guaImage.y) 
    }

    // events
    window.addEventListener('keydown', function(event){
        g.keydowns[event.key] = true
    })
    window.addEventListener('keyup', function(event){
        g.keydowns[event.key] = false
    })
    //
    g.registerAction = function(key, callback) {
        g.actions[key] = callback
    }

    // timer
    setInterval(function(){
        // events
        var actions = Object.keys(g.actions)
        for (let i = 0; i < actions.length; i++) {
            var key = actions[i]
            if (g.keydowns[key]) {
                // 如果按键被按下,调用注册的actions
                g.actions[key]()
            }
        }

        // update
        g.update()
        // clear
        context.clearRect(0, 0, canvas.width, canvas.height)
        // draw
        g.draw()
    }, 1000/30)

    return g
}

var __main = function() {
    var game = GuaGame()

    var paddle = Paddle()

    var ball = Ball()
    // events
    game.registerAction('ArrowLeft', function(){
        paddle.moveLeft()
    })
    game.registerAction('ArrowRight', function(){
        paddle.moveRight()
    })
    game.registerAction('f', function(){
        ball.fire()
    })

    game.update = function() {
        ball.move()
        // 判断相撞
        if (paddle.collide(ball)) { 
            ball.speedY *= -1
        }
    }

    game.draw = function() {
        // draw 
        game.drawImage(paddle)
        game.drawImage(ball)
    }

}


__main()

</script>
    </body>
</html> 


说明

    目前的程序主要是由三部分构成吧。

    首先是paddle挡板部分,有横纵坐标以及速度等一些属性,向左向右移动的函数和碰撞检测,那个碰撞检测写的不是很好,经常出bug,后期再完善一下吧。

    第二就是ball球那部分,它的速度分为X和Y两个方向,还有一个fired标记,意味着开始游戏时按“f”球才会移动。球的move移动函数包含了对画板四周的检测。

    第三部分就是那个GuaGame,也是我眼中整个程序最精妙的地方。

    // draw
    g.drawImage = function(guaImage) {
        this.context.drawImage(guaImage.image, guaImage.x, guaImage.y) 
    }

    它提供了一个接口,可以方便的显示图片。

        var g = {
        actions: {},
        keydowns: {},
    }
        g.registerAction = function(key, callback) {
        g.actions[key] = callback
    }

    这大概就是最令我感到眼前一亮的地方了,registerAction是一个注册事件的函数,它接受按键的键值和一个回调函数,作用就是把键值和它应该执行的函数对应并储存起来。

    // events
    window.addEventListener('keydown', function(event){
        g.keydowns[event.key] = true
    })
    window.addEventListener('keyup', function(event){
        g.keydowns[event.key] = false
    })

    然后是对按键事件的监听,通过keydown和keyup改变键值对应事件的状态,如果为true就执行注册过的回调函数,反之则不执行。

    // timer
    setInterval(function(){
        // events
        var actions = Object.keys(g.actions)
        for (let i = 0; i < actions.length; i++) {
            var key = actions[i]
            if (g.keydowns[key]) {
                // 如果按键被按下,调用注册的actions
                g.actions[key]()
            }
        }

        // update
        g.update()
        // clear
        context.clearRect(0, 0, canvas.width, canvas.height)
        // draw
        g.draw()
    }, 1000/30)
    这个定时器内部判断按键的状态,再调用按键注册的回调函数。clear()清屏,update()和draw()均在GuaGame外部实现。


var __main = function() {
    var game = GuaGame()

    var paddle = Paddle()

    var ball = Ball()
    // events
    game.registerAction('ArrowLeft', function(){
        paddle.moveLeft()
    })
    game.registerAction('ArrowRight', function(){
        paddle.moveRight()
    })
    game.registerAction('f', function(){
        ball.fire()
    })

    game.update = function() {
        ball.move()
        // 判断相撞
        if (paddle.collide(ball)) { 
            ball.speedY *= -1
        }
    }

    game.draw = function() {
        // draw 
        game.drawImage(paddle)
        game.drawImage(ball)
    }

}

    __main()函数作为整个程序的入口,先实例化game\paddle\ball,然后注册回调函数,将键值和函数一一对应起来储存在game的内部,最后实现update和draw函数。

后记

    虽然程序看起来简陋界面也不美观,但学到了一种处理事件的很巧妙的方法,以后在这个框架上完善会变得简单,反而是这个框架的建立很费时间,不过这也没什么好奇怪的,毕竟从0到1可比从1到100难多了。

猜你喜欢

转载自blog.csdn.net/qq_40950957/article/details/80547058