观摩一下《编程之美》:“程序虽然很难写,却很美妙。要想把程序写好,需要写好一定的基础知识,包括编程语言、数据结构与算法。程序写得好,需要缜密的逻辑思维能力和良好的梳理基础,而且熟悉编程环境和编程工具。”
学了几年的计算机,你有没有爱上编程。话说,没有尝试自己写过一个游戏,算不上热爱编程。
俄罗斯方块曾经造成的轰动与造成的经济价值可以说是游戏史上的一件大事,它看似简单但却变化无穷,令人上瘾。相信大多数同学,曾经为它痴迷得茶不思饭不想。
游戏规则
1、一个用于摆放小型正方形的平面虚拟场地,其标准大小:行宽为10,列高为20,以每个小正方形为单位。
2、一组由4个小型正方形组成的规则图形,英文称为Tetromino,中文通称为方块共有7种,分别以S、Z、L、J、I、O、T这7个字母的形状来命名。
I:一次最多消除四层
J(左右):最多消除三层,或消除二层
L:最多消除三层,或消除二层
O:消除一至二层
S(左右):最多二层,容易造成孔洞
Z (左右):最多二层,容易造成孔洞
T:最多二层
方块会从区域上方开始缓慢继续落下。玩家可以以90度为单位旋转方块,以格子为单位左右移动方块,让方块加速落下。方块移到区域最下方或是着地到其他方块上无法移动时,就会固定在该处,而新的方块出现在区域上方开始落下。当区域中某一列横向格子全部由方块填满,则该列会消失并成为玩家的得分。同时删除的列数越多,得分指数上升。
分析与解法
每块方块落下的过程中,我们可以做:
1)旋转到合适的方向
2)水平移动到某一列
3)垂直下落到底部
首先,需要用一个二维数组,area[18][10]表示18*10的游戏区域。其中,数组中值为0表示空,1表示有方块。
方块一共7种,每种有4种方向。定义activeBlock[4],在编译之前这个数组的值预定算好,在程序中直接使用。
难点
1)边界检查。
1 //检查左边界,尝试着朝左边移动一个,看是否合法。 2 function checkLeftBorder(){ 3 for(var i=0; i<activeBlock.length; i++){ 4 if(activeBlock[i].y==0){ 5 return false; 6 } 7 if(!isCellValid(activeBlock[i].x, activeBlock[i].y-1)){ 8 return false; 9 } 10 } 11 return true; 12 } //同理,需要检测右边界和底边界
2)旋转, 需要数理逻辑, 一个点相对另外一个点旋转90度的问题。
3)定时和监听键盘事件机制让游戏自动运行下去。
1 //开始 2 function begin(e){ 3 e.disabled = true; 4 status = 1; 5 tbl = document.getElementById("area"); 6 if(!generateBlock()){ 7 alert("Game over!"); 8 status = 2; 9 return; 10 } 11 paint(); 12 timer = setInterval(moveDown,1000); 13 } 14 document.onkeydown=keyControl;
程序过程
1)用户点开始->构造一个活动图形, 设置定时器。
1 //当前活动的方块, 它可以左右下移动, 变型。当它触底后, 将会更新area; 2 var activeBlock; 3 //生产方块形状, 有7种基本形状。 4 function generateBlock(){ 5 activeBlock = null; 6 activeBlock = new Array(4); 7 //随机产生0-6数组,代表7种形态。 8 var t = (Math.floor(Math.random()*20)+1)%7; 9 switch(t){ 10 case 0:{ 11 activeBlock[0] = {x:0, y:4}; 12 activeBlock[1] = {x:1, y:4}; 13 activeBlock[2] = {x:0, y:5}; 14 activeBlock[3] = {x:1, y:5}; 15 16 break; 17 } 18 //省略部分代码..............................
53 case 6:{ 54 activeBlock[0] = {x:0, y:5}; 55 activeBlock[1] = {x:1, y:4}; 56 activeBlock[2] = {x:1, y:5}; 57 activeBlock[3] = {x:1, y:6}; 58 break; 59 } 60 } 61 //检查刚生产的四个小方格是否可以放在初始化的位置. 62 for(var i=0; i<4; i++){ 63 if(!isCellValid(activeBlock[i].x, activeBlock[i].y)){ 64 return false; 65 } 66 } 67 return true; 68 }
2)每次向下移动后, 都检查是否触底, 如果触底了, 则尝试消行。
1 //消行 2 function deleteLine(){ 3 var lines = 0; 4 for(var i=0; i<18; i++){ 5 var j=0; 6 for(; j<10; j++){ 7 if(area[i][j]==0){ 8 break; 9 } 10 } 11 if(j==10){ 12 lines++; 13 if(i!=0){ 14 for(var k=i-1; k>=0; k--){ 15 area[k+1] = area[k]; 16 } 17 } 18 area[0] = generateBlankLine(); 19 } 20 } 21 return lines; 22 }
3)完了之后再构造一个活动图形, 再设置定时器。
效果图
有待优化
1)设置不同形状方块的颜色。
思路:在创建方块函数内,设定activeBlockColor颜色,七种不同形态方块颜色各异(除了修改generateBlock方法之外,还需要修改paintarea方法。因为一开始考虑不周全,消除一行后,重绘方块的同时将颜色统一,因此可以考虑移除表格n行,然后在顶部增添n行,以保证没消除方块的完整性)。
2)当当前方块下落时,可以提前查看下一个方块。
思路:将generateBlock方法拆分成两部分,一部分用于随机尝试下一个方块,一部分用于缓存当前所要描绘的方块。当当前方块碰到底部被固定后,下一方块开始描绘,同时又再次随机产生新方块。如此反复。