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代码写出了一个俄罗斯方块游戏,最近看了看,今天在这篇文章里面我把我做的分析整理一下(主要是以注释的形式). 我用 ...
随机推荐
- vs下取得资源文件中的版本信息
在Windows Mobile和Wince(Windows Embedded CE)下开发的产品,有时候需要显示当前产品的版本信息.一般来说,版本信息是保存在资源文件里面的,例如下图: 为了保持一致, ...
- 洛谷P1588 丢失的牛
P1588 丢失的牛 158通过 654提交 题目提供者JOHNKRAM 标签USACO 难度普及/提高- 时空限制1s / 128MB 提交 讨论 题解 最新讨论更多讨论 答案下载下来是对的,但 ...
- ls乱码问题解决
http://note.youdao.com/noteshare?id=c7ff510525b4dadaabb6f6a0a72040cc
- SSM框架使用-wrong
mybatis手册 1. mybatis 绑定错误 如果出现: org.apache.ibatis.binding.BindingException: Invalid bound statement ...
- 基础但是很重要的2-sat POJ 3678
http://poj.org/problem?id=3678 题目大意:就是给你n个点,m条边,每个点都可以取值为0或者1,边上都会有一个符号op(op=xor or and三种)和一个权值c.然后问 ...
- HDU 1402 FFT 大数乘法
$A * B$ FFT模板题,找到了一个看起来很清爽的模板 /** @Date : 2017-09-19 22:12:08 * @FileName: HDU 1402 FFT 大整数乘法.cpp * ...
- R0—New packages for reading data into R — fast
小伙伴儿们有福啦,2015年4月10日,Hadley Wickham大牛(开发了著名的ggplots包和plyr包等)和RStudio小组又出新作啦,新作品readr包和readxl包分别用于R读取t ...
- jq消除网页滚动条
网页有些时候需要能滚动的效果,但是不想要滚动条,我就遇到了这样的需求.自己用jq写了一个垂直滚动条. 纯css也可以实现 .box::-webkit-scrollbar{display:none} 但 ...
- 【leetcode 简单】第四十九题 颠倒二进制位
颠倒给定的 32 位无符号整数的二进制位. 示例: 输入: 43261596 输出: 964176192 解释: 43261596 的二进制表示形式为 000000101001010000011110 ...
- VC字体对话框的初始化
本代码需要先添加类成员 LOGFONT lf; void CMyDlg::OnButton3() { // TODO: Add your control notification handler c ...