背景

在折腾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 进行桌面开发,因此请先安装相关系统:

  1. npm install electron-prebuilt -g

注:本项目采用方案能跨所有平台运行,遇权限问题,请在命令行自行添加sudo 。

源代码:

  1. git clone https://git.oschina.net/zhoutk/Tetris.git
  2. 或者:
  3. git clone https://github.com/zhoutk/Tetris

进入项目目录:

  1. cd Tetris

运行程序:

  1. electron .

关键代码分析

功能尽量内聚,类Block封装所有小方块的操作,canvas 接口函数基本上在这个类中封装着;Tetris类组合了Block,封装了俄罗斯方块的绝大部分操作。

Block类(小方块类)

  1. class Block{
  2. constructor(ctx,fillColor,strokeColor){
  3. this.ctx = ctx; //canvas对象
  4. this.width = BLOCKWIDTH; //小方块边长
  5. this.fillColor = fillColor || 'blue'; //直充颜色
  6. this.strokeColor = strokeColor || 'white'; //描边颜色
  7. }
  8. draw(x,y){ //绘制不方块
  9. this.ctx.save();
  10. this.ctx.fillStyle = this.fillColor;
  11. this.ctx.fillRect(x*this.width + 1,y*this.width + 1,this.width-2,this.width-2)
  12. this.ctx.strokeStyle = this.strokeColor;
  13. this.ctx.strokeRect(x*this.width + 1,y*this.width + 1,this.width-2,this.width-2);
  14. this.ctx.restore();
  15. }
  16. erase(x,y){ //擦除小方块
  17. this.ctx.clearRect(x*this.width , y*this.width , 30, 30)
  18. }
  19. canSee(x,y){ //看某个位置是否为空
  20. let c = this.ctx.getImageData(x*this.width+9,y*this.width+9,1,1)
  21. return c.data[0] | c.data[1] | c.data[2] | c.data[3];
  22. }
  23. getColor(x,y){ //取某个位置上的颜色
  24. let c = this.ctx.getImageData(x*this.width+9,y*this.width+9,1,1)
  25. return 'rgba('+c.data[0]+','+c.data[1]+','+c.data[2]+','+c.data[3]+')';
  26. }
  27. }

Tetris类(俄罗斯方块类)

  1. class Tetris {
  2. constructor(shape,ctx,x,y){
  3. this.data = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]; //方块形状数据
  4. this.shape = shape || 0; //方块形状代码
  5. this.ctx = ctx; //canvas对象
  6. this.x = x || 0; //方块位置数据
  7. this.y = y || 0;
  8. this.block = new Block(ctx, COLORS[shape]); //组合block对象
  9. for(let i = 0; i < SHAPES[this.shape].length; i++){ //方块形状初始化
  10. if(SHAPES[this.shape][i]){
  11. this.data[i % 4][1 + Math.floor(i/4)] = 1;
  12. }
  13. }
  14. }
  15. cleanup(){ ... } //消层,计分
  16. moveNext(){ ... } //方块下移一格
  17. moveLeft(){ ... } //方块左移一格
  18. moveRight(){ ... } //方块右移一格
  19. moveDown(){ ... } //方块移动到底
  20. rotate(){ ... } //方块旋转
  21. canDrawNext(){ ... } //检测新方块是否能放置,游戏结束检测
  22. draw(){ ... } //调用block对象,进行俄罗斯方块绘制
  23. erase(){ ... } //调用block对象,进行俄罗斯方块擦除
  24. canSee{ ... } //调用block对象,进行俄罗斯方块放置检测

Block.canSee

取指定位置象素颜色属性,模拟方块“视觉”。

  1. canSee(x,y){
  2. let c = this.ctx.getImageData(x*this.width+9,y*this.width+9,1,1)
  3. return c.data[0] | c.data[1] | c.data[2] | c.data[3]; //黑色为全零,异或为0时,位置为空,其它表示位置已经被占用。
  4. }

Tetris.cleanup

消层比较复杂,配上注释。

  1. cleanup(){
  2. let h = 19, levelCount = 0; //一次消除层数统计变量
  3. while(h >= 0){ //从最底层一直找到顶
  4. let count = 0; //记录同一层上空位置的数量
  5. for(let i = 0; i< 10; i++){ //遍历一层
  6. if(this.canSee(i,h)){ //位置为空,变量加一
  7. count++;
  8. }
  9. }
  10. if(count == 0){ //层满,需要消除
  11. let level = h; //待消层
  12. levelCount++; //消层数量加一
  13. SOUNDS['score'].play();
  14. while(level >= 0){ //将待消层上面的所有层整体下移一层
  15. let ct = 0; //记录同一层上空位置的数量
  16. for(let j = 0; j < 10; j++){
  17. this.block.erase(j,level); //清除待消层方格
  18. if(this.canSee(j,level-1)){ //空位置统计
  19. ct++;
  20. }else{
  21. let bk = new //取垂直上方方格颜色 Block(this.ctx,this.block.getColor(j,level-1))
  22. bk.draw(j,level) //下移
  23. }
  24. }
  25. if(ct == 10){ //一层都是空位置,整体下移提前完成。
  26. break;
  27. }else{
  28. level--; //楼层上移
  29. }
  30. }
  31. }else if(count == 10){ //一层都是空位置,消层工作提前完成。
  32. break;
  33. }else{
  34. h--;
  35. }
  36. }
  37. return levelCount;
  38. }

Tetris.moveNext

方块下移一层比较复杂,配上注释。

  1. moveNext(){
  2. let flag = true; //为跳出双重循环设置的变量
  3. for(let i = 0; i < 4; i++){ //检测是否能下移
  4. for(let j = 0; j < 4; j++){
  5. if(this.data[i][j] && (j ==3 || this.data[i][j+1] == 0)){
  6. if(!this.canSee(this.x + i, this.y + 1 + j)){
  7. flag = false; //已经到底
  8. break;
  9. }
  10. }
  11. }
  12. if(!flag){
  13. break;
  14. }
  15. }
  16. if(flag){ //下移一层
  17. this.erase();
  18. this.y++;
  19. this.draw();
  20. return true;
  21. }else{ //到底处理
  22. let level = this.cleanup(); //消层处理
  23. if(level > 0){ //消层数量大于零
  24. levels += level; //计分
  25. scores += LVSCS[level]
  26. document.getElementById('levelShow').value = levels;
  27. document.getElementById('scoreShow').value = scores;
  28. if(Math.floor(scores / STEPVAL) != STEP){ //调速度级别
  29. clearInterval(interval)
  30. interval = setInterval( tick, TICKVAL - ++STEP * STEPVAL );
  31. document.getElementById('speedShow').value = STEP + 1;
  32. }
  33. }else{
  34. SOUNDS['down'].play()
  35. }
  36. return false;
  37. }
  38. }

操作及规则

  • 方向上键:旋转

  • 方向左键:左移

  • 方向右键:右移

  • 方向下键:下移

  • 空格键:下移到底

  • 计分:同时消队一层计1分;二层3分;三层3分;四层十分

  • 速度分十个级别,每个级别相差50ms

小结

项目现在处于v1.0.0版本,完成俄罗斯方块游戏的所有基本功能,配了音效。后续我考虑网络对战,人机对战,机器自战。我主要是想做人工智能方面的试验,从让算法自己玩俄罗斯方块开始!

electron写俄罗斯方块游戏(Tetris)的更多相关文章

  1. 从零开始---控制台用c写俄罗斯方块游戏(1)

    从零开始---控制台用c写俄罗斯方块游戏(1) 很少写博文,一来自身知识有限,二来自己知道,已经有很多这样的博文了,三就是因为懒,文笔也一般,四来刚出来工作,时间也不多 之所以写这篇博文,是因为应群里 ...

  2. Javascript写俄罗斯方块游戏

    俄罗斯方块这个游戏也做了移动端的兼容, 这个游戏难点是怎么翻转方块, 自己实现的方式是把方块放到一个二维数组, 然后逆时针旋转二维数组. 也有别的方法,比如直接用一个全局变量代表一个方向, 翻转的时候 ...

  3. 从零开始---控制台用c写俄罗斯方块游戏(2)

    上回说到下移的问题,这篇就说一下刷新的问题 我们控制台输出一般都是一行一行的输出,所以,在输出屏幕的时候,我们一个画面闪到另一个画面的效果 我刚开始弄的是用system("CLS" ...

  4. 【转】shell脚本写的俄罗斯方块游戏

    亲测一个很好玩的shell脚本写的俄罗斯方块游戏,脚本来自互联网 先来讲一下思维流程 一.方块的表示 由于shell不能定义二维数组,所以只能用一维数组表示方块,俄罗斯方块主要可以分为7类,每一类方块 ...

  5. Python 写一个俄罗斯方块游戏

    使用 Python 的 PyGame 库写一个俄罗斯方块游戏的逐步指南 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人 ...

  6. 用C写的俄罗斯方块游戏 By: hoodlum1980 编程论坛

    /************************************ * Desc: 俄罗斯方块游戏 * By: hoodlum1980 * Email: jinfd@126.com * Dat ...

  7. 教你看懂网上流传的60行JavaScript代码俄罗斯方块游戏

    早就听说网上有人仅仅用60行JavaScript代码写出了一个俄罗斯方块游戏,最近看了看,今天在这篇文章里面我把我做的分析整理一下(主要是以注释的形式). 我用C写一个功能基本齐全的俄罗斯方块的话,大 ...

  8. 俄罗斯方块游戏 --- java

    俄罗斯方块游戏 如有疑问请查看:http://zh.wikipedia.org/zh-tw/%E4%BF%84%E7%BD%97%E6%96%AF%E6%96%B9%E5%9D%97 更多疑问请参考: ...

  9. 俄罗斯方块游戏JavaScript代码

    JavaScript代码俄罗斯方块游戏 早就听说网上有人仅仅用60行JavaScript代码写出了一个俄罗斯方块游戏,最近看了看,今天在这篇文章里面我把我做的分析整理一下(主要是以注释的形式). 我用 ...

随机推荐

  1. Codeforces Round #338 (Div. 2) D 数学

    D. Multipliers time limit per test 2 seconds memory limit per test 256 megabytes input standard inpu ...

  2. php使用时间戳保存时间的意义

    时间戳记录的是格林尼治时间,使用date格式化的时候会根据你程序设置的不同时区显示不同的时间. 如果使用具体时间,则还需要进行多一步转换.

  3. linux 查看登录日志

    原文:http://www.cnblogs.com/wangkangluo1/archive/2011/09/23/2185976.html linux查看日志: # cd /var/log # le ...

  4. LeetCode-Max Points on a Line[AC源码]

    package com.lw.leet3; import java.util.HashMap; import java.util.Iterator; import java.util.Map; imp ...

  5. Python学习笔记(四十三)virtualenv (创建一套“隔离”的Python运行环境)

    摘抄自:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432712108 ...

  6. logstash 收集 IIS 日志实践

    IIS日志示例: 2017-02-20 00:55:40 127.0.0.1 GET /MkWebAPI/swagger/ui/index - 80 - 127.0.0.1 Mozilla/5.0+( ...

  7. 【BZOJ】3998: [TJOI2015]弦论

    [题意]给定长度为n的小写字母字符串S,求第k小子串.n<=5*10^5. 给定T,T=0时不同位置的相同子串算一个,T=1时算多个. [算法]后缀自动机 [题解]对S建立SAM,T=0则每个节 ...

  8. Spring Boot中对log4j进行多环境不同日志级别的控制

    之前介绍了在<Spring boot中使用log4j记录日志>,仅通过log4j.properties对日志级别进行控制,对于需要多环境部署的环境不是很方便,可能我们在开发环境大部分模块需 ...

  9. H5调试工具 - weinre远程调试工具

    weinre 简介 weinre 是一款类似于firebug 和Web Inspector的网页调试工具, 它的不同之处在于可以用于进行远程调试,比如调试手机上面的网页. 安装 weinre(运行在n ...

  10. java使用simpleDateFormat格式化日期 时间

    时间日期标识符: yyyy:年 MM:月 dd:日 hh:1~12小时制(1-12) HH:24小时制(0-23) mm:分 ss:秒 S:毫秒 E:星期几 D:一年中的第几天 F:一月中的第几个星期 ...