electron写俄罗斯方块游戏(Tetris)
背景
在折腾ES6,突然想起大学时用c语言写过俄罗斯方块,本项目中主要是利用ES6的Class特性进行面向对象编程。项目采用node.js v6.2.0 + electron v1.1.0 进行桌面开发,能跨所有平台运行。
思路
全面应用面向对象的设计思想,让功能内聚性强。
把七种方块想成独立的“生物”对象,让它能“看”到周围的世界。
没有使用传统的大的二维数组来表示游戏场面状态,而是让tetris自己去“看”。
使用html5的canvas来完成,比较象cgi编程。
使用最少的canvas特性,只用了fillRect,strokeRect,getImageData,clearRect等几个函数。
效果图
我玩的最高纪录^_^
运行方法
项目采用node.js v6.2.0 + electron v1.1.0 进行桌面开发,因此请先安装相关系统:
npm install electron-prebuilt -g
注:本项目采用方案能跨所有平台运行,遇权限问题,请在命令行自行添加sudo 。
源代码:
git clone https://git.oschina.net/zhoutk/Tetris.git
或者:
git clone https://github.com/zhoutk/Tetris
进入项目目录:
cd Tetris
运行程序:
electron .
关键代码分析
功能尽量内聚,类Block封装所有小方块的操作,canvas 接口函数基本上在这个类中封装着;Tetris类组合了Block,封装了俄罗斯方块的绝大部分操作。
Block类(小方块类)
class Block{
    constructor(ctx,fillColor,strokeColor){
        this.ctx = ctx;                              //canvas对象
        this.width = BLOCKWIDTH;                     //小方块边长
        this.fillColor = fillColor || 'blue';        //直充颜色
        this.strokeColor = strokeColor || 'white';   //描边颜色
    }
    draw(x,y){                                       //绘制不方块
        this.ctx.save();
        this.ctx.fillStyle = this.fillColor;
        this.ctx.fillRect(x*this.width + 1,y*this.width + 1,this.width-2,this.width-2)
        this.ctx.strokeStyle = this.strokeColor;
        this.ctx.strokeRect(x*this.width + 1,y*this.width + 1,this.width-2,this.width-2);
        this.ctx.restore();
    }
    erase(x,y){                                        //擦除小方块
        this.ctx.clearRect(x*this.width , y*this.width , 30, 30)
    }
    canSee(x,y){                                        //看某个位置是否为空
        let c = this.ctx.getImageData(x*this.width+9,y*this.width+9,1,1)
        return c.data[0] | c.data[1] | c.data[2] | c.data[3];
    }
    getColor(x,y){                                      //取某个位置上的颜色
        let c = this.ctx.getImageData(x*this.width+9,y*this.width+9,1,1)
        return 'rgba('+c.data[0]+','+c.data[1]+','+c.data[2]+','+c.data[3]+')';
    }
}
Tetris类(俄罗斯方块类)
class Tetris {
    constructor(shape,ctx,x,y){
        this.data = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]];    //方块形状数据
        this.shape = shape || 0;                                  //方块形状代码
        this.ctx = ctx;                                            //canvas对象
        this.x =  x || 0;                                        //方块位置数据
        this.y =  y || 0;
        this.block = new Block(ctx, COLORS[shape]);              //组合block对象
        for(let i = 0; i < SHAPES[this.shape].length; i++){      //方块形状初始化
            if(SHAPES[this.shape][i]){
                this.data[i % 4][1 + Math.floor(i/4)] = 1;
            }
        }
    }
    cleanup(){ ... }                                            //消层,计分
    moveNext(){ ... }                                           //方块下移一格
    moveLeft(){ ... }                                           //方块左移一格
    moveRight(){ ... }                                          //方块右移一格
    moveDown(){ ... }                                           //方块移动到底
    rotate(){ ... }                                             //方块旋转
    canDrawNext(){ ... }                                        //检测新方块是否能放置,游戏结束检测
    draw(){ ... }                                               //调用block对象,进行俄罗斯方块绘制
    erase(){ ... }                                               //调用block对象,进行俄罗斯方块擦除
    canSee{ ... }                                               //调用block对象,进行俄罗斯方块放置检测
    
Block.canSee
取指定位置象素颜色属性,模拟方块“视觉”。
canSee(x,y){
        let c = this.ctx.getImageData(x*this.width+9,y*this.width+9,1,1)
        return c.data[0] | c.data[1] | c.data[2] | c.data[3];        //黑色为全零,异或为0时,位置为空,其它表示位置已经被占用。
    }
Tetris.cleanup
消层比较复杂,配上注释。
cleanup(){
        let h = 19, levelCount = 0;               //一次消除层数统计变量
        while(h >= 0){                            //从最底层一直找到顶
            let count = 0;                        //记录同一层上空位置的数量
            for(let i = 0; i< 10; i++){           //遍历一层
                if(this.canSee(i,h)){             //位置为空,变量加一
                    count++;
                }
            }
            if(count == 0){                       //层满,需要消除
                let level = h;                    //待消层
                levelCount++;                     //消层数量加一
                SOUNDS['score'].play();
                while(level >= 0){                //将待消层上面的所有层整体下移一层
                    let ct = 0;                    //记录同一层上空位置的数量
                    for(let j = 0; j < 10; j++){
                        this.block.erase(j,level);     //清除待消层方格
                        if(this.canSee(j,level-1)){    //空位置统计
                            ct++;
                        }else{
                            let bk = new               //取垂直上方方格颜色          Block(this.ctx,this.block.getColor(j,level-1))
                            bk.draw(j,level)           //下移
                        }
                    }
                    if(ct == 10){                       //一层都是空位置,整体下移提前完成。
                        break;
                    }else{
                        level--;                        //楼层上移
                    }
                }
            }else if(count == 10){                   //一层都是空位置,消层工作提前完成。
                break;
            }else{
                h--;
            }
        }
        return levelCount;
    }
Tetris.moveNext
方块下移一层比较复杂,配上注释。
moveNext(){
        let flag = true;                                    //为跳出双重循环设置的变量
        for(let i = 0; i < 4; i++){                         //检测是否能下移
            for(let j = 0; j < 4; j++){
                if(this.data[i][j] && (j ==3 || this.data[i][j+1] == 0)){
                    if(!this.canSee(this.x + i, this.y + 1 + j)){
                        flag = false;                      //已经到底
                        break;
                    }
                }
            }
            if(!flag){
                break;
            }
        }
        if(flag){                                        //下移一层
            this.erase();
            this.y++;
            this.draw();
            return true;
        }else{                                            //到底处理
            let level = this.cleanup();                   //消层处理
            if(level > 0){                                //消层数量大于零
                levels += level;                          //计分
                scores += LVSCS[level]
                document.getElementById('levelShow').value = levels;
                document.getElementById('scoreShow').value = scores;
                if(Math.floor(scores / STEPVAL) != STEP){  //调速度级别
                    clearInterval(interval)
                    interval = setInterval( tick, TICKVAL - ++STEP * STEPVAL );
                    document.getElementById('speedShow').value = STEP + 1;
                }
            }else{
                SOUNDS['down'].play()
            }
            return false;
        }
    }
操作及规则
方向上键:旋转
方向左键:左移
方向右键:右移
方向下键:下移
空格键:下移到底
计分:同时消队一层计1分;二层3分;三层3分;四层十分
速度分十个级别,每个级别相差50ms
小结
项目现在处于v1.0.0版本,完成俄罗斯方块游戏的所有基本功能,配了音效。后续我考虑网络对战,人机对战,机器自战。我主要是想做人工智能方面的试验,从让算法自己玩俄罗斯方块开始!
electron写俄罗斯方块游戏(Tetris)的更多相关文章
- 从零开始---控制台用c写俄罗斯方块游戏(1)
		
从零开始---控制台用c写俄罗斯方块游戏(1) 很少写博文,一来自身知识有限,二来自己知道,已经有很多这样的博文了,三就是因为懒,文笔也一般,四来刚出来工作,时间也不多 之所以写这篇博文,是因为应群里 ...
 - Javascript写俄罗斯方块游戏
		
俄罗斯方块这个游戏也做了移动端的兼容, 这个游戏难点是怎么翻转方块, 自己实现的方式是把方块放到一个二维数组, 然后逆时针旋转二维数组. 也有别的方法,比如直接用一个全局变量代表一个方向, 翻转的时候 ...
 - 从零开始---控制台用c写俄罗斯方块游戏(2)
		
上回说到下移的问题,这篇就说一下刷新的问题 我们控制台输出一般都是一行一行的输出,所以,在输出屏幕的时候,我们一个画面闪到另一个画面的效果 我刚开始弄的是用system("CLS" ...
 - 【转】shell脚本写的俄罗斯方块游戏
		
亲测一个很好玩的shell脚本写的俄罗斯方块游戏,脚本来自互联网 先来讲一下思维流程 一.方块的表示 由于shell不能定义二维数组,所以只能用一维数组表示方块,俄罗斯方块主要可以分为7类,每一类方块 ...
 - Python 写一个俄罗斯方块游戏
		
使用 Python 的 PyGame 库写一个俄罗斯方块游戏的逐步指南 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人 ...
 - 用C写的俄罗斯方块游戏  By:    hoodlum1980   编程论坛
		
/************************************ * Desc: 俄罗斯方块游戏 * By: hoodlum1980 * Email: jinfd@126.com * Dat ...
 - 教你看懂网上流传的60行JavaScript代码俄罗斯方块游戏
		
早就听说网上有人仅仅用60行JavaScript代码写出了一个俄罗斯方块游戏,最近看了看,今天在这篇文章里面我把我做的分析整理一下(主要是以注释的形式). 我用C写一个功能基本齐全的俄罗斯方块的话,大 ...
 - 俄罗斯方块游戏 --- java
		
俄罗斯方块游戏 如有疑问请查看:http://zh.wikipedia.org/zh-tw/%E4%BF%84%E7%BD%97%E6%96%AF%E6%96%B9%E5%9D%97 更多疑问请参考: ...
 - 俄罗斯方块游戏JavaScript代码
		
JavaScript代码俄罗斯方块游戏 早就听说网上有人仅仅用60行JavaScript代码写出了一个俄罗斯方块游戏,最近看了看,今天在这篇文章里面我把我做的分析整理一下(主要是以注释的形式). 我用 ...
 
随机推荐
- Codeforces 864E dp
			
题意: 房间着火了,里面有n件物品,每件物品有营救需要的时间t,被烧坏的最晚时间d,他的价值p,问能得到的最大价值,并且输出营救出来的物品编号 代码: //必然是先救存活时间短的即d小的,所以先排个序 ...
 - 「Django」学习之路,持续更改
			
一.setting设置 1.设置 局域网可以部署连接 ALLOWED_HOSTS = ['*.besttome.com','192.168.1.100'] 2.static配置 STATIC_URL ...
 - 赤池信息量准则 ( Akaike information criterion)
			
sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...
 - DOM操作二三事
			
我突然想起了append(),但是我记不太清它是原生JS的还是jQuery封装的,貌似是JS的,咦?那它在jQuery里叫什么来着?哎呀!记不清了!确定append()是JS里的?不是jQuery里的 ...
 - angular 开发之proxy
			
创建proxy配置文件proxy.conf.json 内容如下 { "/api/*": { "target": "https://abc.com ...
 - 【CODEVS】1022 覆盖
			
[算法]二分图匹配(最大流) [题解]对i+j进行奇偶染色,就可以保证相邻两格异色. 然后就是二分图了,对相邻格子连边跑最大流即可. #include<cstdio> #include&l ...
 - BZOJ做题记录[0512~?]
			
觉得做一道开一篇真不好...好多想找的东西都被刷下去了... 至于?的日期究竟到什么时候...还是看心情...但是估计不会超过七天吧 最后更新时间:05/19 10:42 [05/14 10:56]我 ...
 - 【BZOJ】3039: 玉蟾宫 悬线法
			
[题意]给定01矩阵,求最大全1子矩阵.n,m<=1000. [算法]动态规划(悬线法) [题解]★对于01矩阵中的任意一个全1极大子矩阵,都可以在其上边界遇到的障碍点处悬线到下边界的点x,则点 ...
 - MySql 快速去重方法
			
1.复制需要去重的表 CREATE TABLE 新表 LIKE 旧表 ; 2.将需要去重的字段 设置为唯一union 索引 ALTER TABLE 表名 ADD UNIQUE(`字段`); 3.复制旧 ...
 - 通过删除hbase表中的region来达到删除表中数据
			
公司最近在搞一个hbase删除数据,由于在建表的时候是通过region来对每日的数据进行存储的,所以要求在删除的时候直接通过删除region的来删除数据(最好的方案是只删除region中的数据,不把r ...