侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Html5原创俄罗斯方块(基于canvas)

前端  /  管理员 发布于 6年前   445

第一次写俄罗斯方块的时候已经是1年多前了,也是我刚刚学js不久。

为了加强对js的理解又加上对游戏的爱好,于是在没有参考他人的思路和代码下,自己用最基本的js代码写出了基于canvas的俄罗斯方块。

在大三的暑假,我又用了es6的语法进行了改进,包含了class的语法糖、箭头函数等,进一步增加自己对es6的理解,代码有400+行

想要做这个小游戏,必须先熟悉H5的canvas,js对数组的处理,键盘事件监听和处理,定时器的使用等,其他的就是基本的逻辑处理了。

游戏的规则就是核心,也是我们代码的重中之重

这里的逻辑核心是需要判断方块是否碰撞(当前运动的方块和已经定位好的方块有碰撞以致于当前的运动的方块不能在向下走,因为我们的方块默认是向下走的,如果不能向下走,是视为已经定位好的方块,然后在生成一个新的方块从初始位置继续往下走)。

而且这个碰撞还需要应用在方块变形的时候,同样地,如果方块在变形的过程中和其他定位好的方块进行碰撞,则我们应该阻止这个方块进行变形成功,

附上代码,欢迎讨论和指正

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>es6-重构俄罗斯方块(基于canvas)</title>    <style type="text/css">        #tetris{ margin: 10px 250px;}    </style></head><body>    <canvas width="700" height="525" id="tetris"></canvas>    <div id="text" style='color: red;font-size: 30px;'>当前分数:0</div>    <script type="text/javascript">        /**         * [一个完整的俄罗斯方块类 design by magic_xiang]         * @param  {number} side     [每个方块边长(px),默认35]         * @param  {number} width     [一行包含的方块数(个),默认20]         * @param  {number} height     [一列包含的方块数(个),默认15]         * @param  {number} speed     [方块下落移动速度(ms),默认400]         */        class tetris{constructor(side=35, width=20, height=15, speed=400){    this.side = side// 每个方块边长    this.width = width// 一行包含的方块数    this.height = height        // 一列包含的方块数    this.speed = speed // 方块下落移动速度    this.num_blcok    // 当前方块类型的数字变量    this.type_color    // 当前颜色类型的字符串变量    this.ident        // setInterval的标识    this.direction = 1// 方块方向,初始化为1,默认状态        this.grade = 0    // 用来计算分数    this.over = false// 游戏是否结束    this.arr_bX = []// 存放当前方块的X坐标    this.arr_bY = []// 存放当前方块的Y坐标    this.arr_store_X = []        // 存放到达底部所有方块的X坐标    this.arr_store_Y = []        // 存放到达底部所有方块的Y坐标    this.arr_store_color = []    // 存放到达底部所有方块的颜色    this.paints = document.getElementById('tetris').getContext('2d')    //获取画笔    self = this}// 封装paints方法,让代码更简洁paintfr(x, y, scale=1){    this.paints.fillRect(x*this.side, y*this.side, scale*this.side, scale*this.side)}// 游戏开始gameStart(){    this.init()    this.run()}// 初始化工作init(){    this.initBackground()    this.initBlock()}// 方块自动下落run(){    this.ident = setInterval("self.down_speed_up()", this.speed)}// 初始化地图initBackground(){    this.paints.beginPath()    this.paints.fillStyle='#000000'        //地图填充颜色为黑色    for(let i = 0; i < this.height; i++){        for(let j = 0; j < this.width; j++){this.paintfr(j, i)        }    }    this.paints.closePath()}// 初始化方块的位置和颜色initBlock(){    this.paints.beginPath()    this.createRandom('rColor')        //生成颜色字符串,    this.paints.fillStyle = this.type_color    this.createRandom('rBlock')        //生成方块类型数字    this.arr_bX.forEach((item, index) => {        this.paintfr(item, this.arr_bY[index], 0.9)    })    this.paints.closePath()}// 利用数组画方块drawBlock(color){    this.paints.beginPath()    this.paints.fillStyle = color    this.arr_bX.forEach((item, index) => {        this.paintfr(item, this.arr_bY[index], 0.9)    })    this.paints.closePath()}// 画已经在定位好的方块drawStaticBlock(){    this.arr_store_X.forEach((item, index) => {        this.paints.beginPath()        this.paints.fillStyle = this.arr_store_color[index]        this.paintfr(item, this.arr_store_Y[index], 0.9)        this.paints.closePath()    })}// 生成随机数返回方块类型或颜色类型createRandom(type){    let temp = this.width/2-1    if (type == 'rBlock'){         //如果是方块类型        this.num_blcok = Math.round(Math.random()*4+1)        switch(this.num_blcok){case 1:    this.arr_bX.push(temp,temp-1,temp,temp+1)    this.arr_bY.push(0,1,1,1)    breakcase 2:    this.arr_bX.push(temp,temp-1,temp-1,temp+1)    this.arr_bY.push(1,0,1,1)    breakcase 3:    this.arr_bX.push(temp,temp-1,temp+1,temp+2)    this.arr_bY.push(0,0,0,0)    breakcase 4:    this.arr_bX.push(temp,temp-1,temp,temp+1)    this.arr_bY.push(0,0,1,1)    breakcase 5:    this.arr_bX.push(temp,temp+1,temp,temp+1)    this.arr_bY.push(0,0,1,1)    break        }    }    if (type == 'rColor'){ //如果是颜色类型        let num_color = Math.round(Math.random()*8+1)         switch(num_color){case 1:    this.type_color='#3EF72A'    breakcase 2:    this.type_color='yellow'    breakcase 3:    this.type_color='#2FE0BF'    breakcase 4:    this.type_color='red'    breakcase 5:    this.type_color='gray'    breakcase 6:    this.type_color='#C932C6'    breakcase 7:    this.type_color= '#FC751B'    breakcase 8:    this.type_color= '#6E6EDD'    breakcase 9:    this.type_color= '#F4E9E1'    break        }    }}// 判断方块之间是否碰撞(下),以及变形时是否越过下边界judgeCollision_down(){    for(let i = 0; i < this.arr_bX.length; i++){        if (this.arr_bY[i] + 1 == this.height){ //变形时是否越过下边界return false        }         if (this.arr_store_X.length != 0) {    //判断方块之间是否碰撞(下)for(let j = 0; j < this.arr_store_X.length; j++){    if (this.arr_bX[i] == this.arr_store_X[j]) {        if (this.arr_bY[i] + 1 == this.arr_store_Y[j]) {return false        }    }    }        }        }    return true}//判断方块之间是否碰撞(左右),以及变形时是否越过左右边界judgeCollision_other(num){    for(let i = 0; i < this.arr_bX.length; i++){        if (num == 1) {//变形时是否越过右边界if (this.arr_bX[i] == this.width - 1)     return false        }        if (num == -1) {    //变形时是否越过左边界if (this.arr_bX[i] == 0)    return false        }        if (this.arr_store_X.length != 0) {        //判断方块之间是否碰撞(左右)for(let j = 0; j < this.arr_store_X.length; j++){    if (this.arr_bY[i] == this.arr_store_Y[j]) {        if (this.arr_bX[i] + num == this.arr_store_X[j]) {return false        }    }}        }    }    return true;}//方向键为下的加速函数down_speed_up(){    let flag_all_down = true    flag_all_down = this.judgeCollision_down()        if (flag_all_down) {        this.initBackground()        for(let i = 0; i < this.arr_bY.length; i++){this.arr_bY[i] = this.arr_bY[i] + 1        }    }    else{        for(let i=0; i < this.arr_bX.length; i++){this.arr_store_X.push(this.arr_bX[i])this.arr_store_Y.push(this.arr_bY[i])this.arr_store_color.push(this.type_color)        }this.arr_bX.splice(0,this.arr_bX.length)        this.arr_bY.splice(0,this.arr_bY.length)        this.initBlock()    }    this.clearUnderBlock()    this.drawBlock(this.type_color)    this.drawStaticBlock()    this.gameover()}//方向键为左右的左移动函数move(dir_temp){    this.initBackground()    if (dir_temp == 1) {        //右        let flag_all_right = true        flag_all_right = this.judgeCollision_other(1)    if (flag_all_right) {for(let i = 0; i < this.arr_bY.length; i++){    this.arr_bX[i] = this.arr_bX[i]+1}        }    }    else{        let flag_all_left = true        flag_all_left = this.judgeCollision_other(-1)        if (flag_all_left) {for(let i=0; i < this.arr_bY.length; i++){    this.arr_bX[i] = this.arr_bX[i]-1}        }    }    this.drawBlock(this.type_color)    this.drawStaticBlock()}//方向键为空格的变换方向函数up_change_direction(num_blcok){     if (num_blcok == 5) {        return    }        let arr_tempX = []    let arr_tempY = []    //因为不知道是否能够变形成功,所以先存储起来    for(let i = 0;i < this.arr_bX.length; i++){arr_tempX.push(this.arr_bX[i])        arr_tempY.push(this.arr_bY[i])    }    this.direction++    //将中心坐标提取出来,变形都以当前中心为准    let ax_temp = this.arr_bX[0]        let ay_temp = this.arr_bY[0]        this.arr_bX.splice(0, this.arr_bX.length)//将数组清空     this.arr_bY.splice(0, this.arr_bY.length)    if (num_blcok == 1) {    switch(this.direction%4){case 1:    this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp+1)    this.arr_bY.push(ay_temp,ay_temp+1,ay_temp+1,ay_temp+1)    breakcase 2:    this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp)    this.arr_bY.push(ay_temp,ay_temp,ay_temp-1,ay_temp+1)    breakcase 3:    this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp+1)    this.arr_bY.push(ay_temp,ay_temp,ay_temp+1,ay_temp)    breakcase 0:    this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp+1)    this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp)    break        }    }    if (num_blcok == 2) {    switch(this.direction%4){case 1:    this.arr_bX.push(ax_temp,ax_temp-1,ax_temp-1,ax_temp+1)    this.arr_bY.push(ay_temp,ay_temp,ay_temp-1,ay_temp)    breakcase 2:    this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp-1)    this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp+1)    breakcase 3:    this.arr_bX.push(ax_temp,ax_temp-1,ax_temp+1,ax_temp+1)    this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp+1)    breakcase 0:    this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp+1)    this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp-1)    break        }    }    if (num_blcok == 3) {    switch(this.direction%4){case 1:    this.arr_bX.push(ax_temp,ax_temp-1,ax_temp+1,ax_temp+2)    this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp)    breakcase 2:    this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp)    this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp+2)    breakcase 3:    this.arr_bX.push(ax_temp,ax_temp-1,ax_temp+1,ax_temp+2)    this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp)    breakcase 0:    this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp)    this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp+2)    break        }    }    if (num_blcok == 4) {    switch(this.direction%4){case 1:    this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp+1)    this.arr_bY.push(ay_temp,ay_temp,ay_temp+1,ay_temp+1)    breakcase 2:    this.arr_bX.push(ax_temp,ax_temp,ax_temp+1,ax_temp+1)    this.arr_bY.push(ay_temp,ay_temp+1,ay_temp,ay_temp-1)    breakcase 3:    this.arr_bX.push(ax_temp,ax_temp,ax_temp-1,ax_temp+1)    this.arr_bY.push(ay_temp,ay_temp-1,ay_temp,ay_temp-1)    breakcase 0:    this.arr_bX.push(ax_temp,ax_temp,ax_temp+1,ax_temp+1)    this.arr_bY.push(ay_temp,ay_temp-1,ay_temp,ay_temp+1)    break        }    }        if (! (this.judgeCollision_other(-1) && this.judgeCollision_down() && this.judgeCollision_other(1)  )) {//如果变形不成功则执行下面代码        this.arr_bX.splice(0, this.arr_bX.length)         this.arr_bY.splice(0, this.arr_bY.length)        for(let i=0; i< arr_tempX.length; i++){this.arr_bX.push(arr_tempX[i])this.arr_bY.push(arr_tempY[i])        }    }    this.drawStaticBlock()}//一行满了清空方块,上面方块Y坐标+1clearUnderBlock(){    //删除低层方块    let arr_row=[]    let line_num    if (this.arr_store_X.length != 0) {        for(let j = this.height-1; j >= 0; j--){for(let i = 0; i < this.arr_store_color.length; i++){    if (this.arr_store_Y[i] == j) {        arr_row.push(i)    }}if (arr_row.length == this.width) {    line_num = j    break}else{    arr_row.splice(0, arr_row.length)}        }    }if (arr_row.length == this.width) {        //计算成绩grade        this.grade++    document.getElementById('text').innerHTML = '当前成绩:'+this.grade        for(let i = 0; i < arr_row.length; i++){this.arr_store_X.splice(arr_row[i]-i, 1)this.arr_store_Y.splice(arr_row[i]-i, 1)this.arr_store_color.splice(arr_row[i]-i, 1)        }//让上面的方块往下掉一格        for(let i = 0; i < this.arr_store_color.length; i++){if (this.arr_store_Y[i] < line_num) {    this.arr_store_Y[i] = this.arr_store_Y[i]+1}        }    }}//判断游戏结束gameover(){    for(let i=0; i < this.arr_store_X.length; i++){        if (this.arr_store_Y[i] == 0) {clearInterval(this.ident)this.over = true        }    }}        }        let tetrisObj = new tetris()        tetrisObj.gameStart()    //方向键功能函数        document.onkeydown = (e) => {   if (tetrisObj.over)    returnswitch(e.keyCode){    case 40:  // 方向为下        tetrisObj.down_speed_up()        break    case 32:  // 空格换方向        tetrisObj.initBackground()        //重画地图        tetrisObj.up_change_direction(tetrisObj.num_blcok)        tetrisObj.drawBlock(tetrisObj.type_color)        break    case 37:  // 方向为左        tetrisObj.initBackground()        tetrisObj.move(-1)        tetrisObj.drawBlock(tetrisObj.type_color)        break    case 39:  // 方向为右        tetrisObj.initBackground()        tetrisObj.move(1)        tetrisObj.drawBlock(tetrisObj.type_color)        break}}    </script></body></html>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


  • 上一条:
    workerman怎么访问
    下一条:
    Ruby简明教程之循环语句介绍
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 使用 Alpine.js 排序插件对元素进行排序(0个评论)
    • 在js中使用jszip + file-saver实现批量下载OSS文件功能示例(0个评论)
    • 在vue中实现父页面按钮显示子组件中的el-dialog效果(0个评论)
    • 使用mock-server实现模拟接口对接流程步骤(0个评论)
    • vue项目打包程序实现把项目打包成一个exe可执行程序(0个评论)
    • 近期文章
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-10
    • 2017-11
    • 2018-03
    • 2018-04
    • 2018-05
    • 2018-06
    • 2018-09
    • 2018-11
    • 2018-12
    • 2019-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2021-04
    • 2021-05
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客