canvas制作网页贪吃蛇学习与扩展

一、项目来源:uniquemo

已果断star

二、学习笔记

1、在html中同时引入多个js文件顺序问题

由于html为由上至下顺序执行,若parent.js引用了child.js,需将child.js先引入html文件中,以便parent.js使用。
通常child.js为构造函数,parent.js为主执行函数。
例如:

    <script src="./Snake.js"></script>
    <script src="./index.js"></script>

其中index.js中使用了Snake构造函数。
若将二者调换顺序,不会报错,但主函数将不会执行。

2、检测键盘点击事件:key()

菜鸟教程-key()方法

idea:键盘呈现脚本——实时展示被按下按键

3、简化名称表达:replace()

W3school-JavaScript replace()方法

4、计时器:setInterval()

W3shool-HTML DOM setInterval() 方法
用作周期性调用某一段代码
可令

	let timer = null
	timer = setInterval( () => {
		console.log("hello,World!")
		}, 1000)

停止

	clearInterval(timer)

三、新增功能

1、触壁结束

(1)构造函数 Snake.js

  • 判断触壁
Snake.prototype.touchWall = function() {
    const { width, height } = this.canvasOptions.canvas
    if( this.x < 0 || this.y < 0 || this.x > width || this.y > height ){
        this.die()
        return true
    }
    return false
}
  • 死亡变色
Snake.prototype.die = function() {
    ctx.fillStyle = '#ffaa33'
    for(let i = 0; i< this.tails.length; i++){
        const {x, y} = this.tails[i]
        ctx.fillRect(x, y, this.size, this.size)
    }
}

遍历重新绘制贪吃蛇,改变颜色

(2)主函数 index.js

  • start()中添加结束判断条件
function start() {
    timer = setInterval(() => {
    
		...
		
        if(snake.touchWall()){
            gameOver()
        }
    }, 150 )
}
  • 结束执行函数
function gameOver(){
    clearInterval(timer)
    ctx.font = 3 * size + "px Arial"
    ctx.fillStyle = '#fff'
    ctx.textAlign = 'center'
    ctx.textBaseLine = 'middle'
    ctx.fillText( "GAME OVER !!", canvas.width / 2, canvas.height / 2 )    
}

画布绘制‘GAME OVER’文字

四、优化

1、禁止回退&向前加速

  • 禁止贪吃蛇向反方向爬行,保持头在前尾在后
  • 同方向再次按键则向前加速一倍
  • 在Snake中添加accelerate属性,定义画布刷新周期;
    在原项目中,画布每刷新一次,贪吃蛇向前运动一格,刷新周期变短,速度变快。
this.accelerate = 150

(1)更新改变方向函数

  • 判断当键盘按键与当前运动方向相反时,提示不能后退。
  • 判断当键盘按键与当前运动方向相同时,进行加速。
  • 在加速状态下改变有效方向,速度更新为最低档。
Snake.prototype.changeDirection = function(direction) {
    switch(direction) {
        case 'Up':
            if(this.xSpeed == 0 && this.ySpeed > 0 ) {
                console.log ( 'You can not go back !' )
            }
            else if(this.xSpeed == 0 && this.ySpeed < 0) {
                this.speedUp()  
            }
            else {
                this.accelerate = 150
                this.xSpeed = 0
                this.ySpeed = -size
            }
            break
        case 'Down':
            if(this.xSpeed == 0 && this.ySpeed < 0 ){
                console.log ( 'You can not go back !' )
            }
            else if(this.xSpeed == 0 && this.ySpeed > 0) {
                this.speedUp()
            }
            else {
                this.accelerate = 150
                this.xSpeed = 0
                this.ySpeed = size
            }
            break
        case 'Left':
            if(this.xSpeed > 0 && this.ySpeed == 0 ){
                console.log ( 'You can not go back !' )
            }
            else if(this.xSpeed < 0 && this.ySpeed == 0) {
                this.speedUp()
            }
            else {
                this.accelerate = 150
                this.xSpeed = -size
                this.ySpeed = 0
            }
            break
        case 'Right':
            if(this.xSpeed < 0 && this.ySpeed == 0 ){
                console.log ( 'You can not go back !' )
            }
            else if(this.xSpeed > 0 && this.ySpeed == 0) {
                this.speedUp()
            }
            else {
                this.accelerate = 150
                this.xSpeed = size
                this.ySpeed = 0
            }
            break
      }
      console.log(this.accelerate)
}

(2)加速函数

  • 加速次数提示
  • 设置加速上限,防止速度过快。
Snake.prototype.speedUp = function() {
    if(this.accelerate == 50) {
        console.log('SPEED LIMIT !!')
    }
    else {
        this.accelerate = this.accelerate - 50    
        console.log (`Speed Up: ${(150-this.accelerate)/50} times`)
    }
}

主程序中

let lastSpeed = snake.accelerate

(3)主程序中实时检测速度变化

  • 设置frequency参数,用于接收刷新周期,达到改变速度的目的。
  • setInterval()方法中的刷新周期不能在函数进行时改变,只能先停止执行(clearInterval()),更新周期后再开始刷新。
    刷新时通过if( lastSpeed != snake.accelerate )判断速度是否发生改变,若发生改变,则停止当前timer,更新速度。
function motion(frequency) {
    timer = setInterval(() => {
        ctx.clearRect(0, 0, canvas.width, canvas.height)
        target.draw()
        snake.update()
        snake.draw()

        if(snake.eatTarget(target)){
            target.genRandomLocation()
        }
        snake.checkCollision()
        document.getElementById('score').innerHTML = snake.targetNum
        if(snake.touchWall()){
            gameOver()
        }
        if( lastSpeed != snake.accelerate ){
            clearInterval(timer)
            start()
        }
    }, frequency)
}
  • 将timer与start()分离
  • 停止刷新后更新速度,对timer计时器传入新的刷新周期,并重新开始执行。
function start() {
    console.log(`current speed: ${snake.accelerate}`)
    lastSpeed = snake.accelerate
    motion(lastSpeed)
}

2、重新开始

(1)参数清零 Snake.js

Snake.prototype.init= function() {
    this.x = 0
    this.y = 0
    this.xSpped = size * 1
    this.ySpeed = 0
    this.targetNum = 0
    this.tails = []
}

(2)主函数初始化

  • 加入snake.init函数
function init() {
    target.genRandomLocation()
    snake.init()
    target.draw()
    snake.draw()
}

(3)清空画布、加入初始化函数init()

startBtn.addEventListener('click', () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    init()
    start()
})

写在最后

本人初入前端,许多代码写的比较冗余或含有隐藏bug或造成性能损耗,希望大家评论指出;如有讲述不明之处,也可评论询问,我会尽自己最大所能回复大家!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值