俄罗斯方块小游戏
俄罗斯方块小游戏
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_26239525/article/details/82431921
研一时写的俄罗斯方块小游戏,C++程序,代码还有很多值得优化的地方,欢迎交流。
编程环境:Based on Qt 5.9.1 (MSVC 2015)。
首先要了解俄罗斯方块的游戏规则:
- 俄罗斯方块的游戏基本规则通过左右上下按键来移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。
- 游戏中使用键盘方向键←→控制移动,↑变形,↓加速下落,空格开始。
- 由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。
- 这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。
- 没有被消除掉的方块不断堆积起来, 一旦堆到屏幕顶端,玩家便告输,游戏结束。
然后此程序中俄罗斯方块实现的基本思路:
- 俄罗斯方块是一张表格,很容易想到用一个二维数组来实现。然后通过数组元素判断此处是否有方块,1代表有方块,0代表没有方块(为了好看,两边和底是1的方块没有显示出来)。
- 然后二维数组在定义的时候定义成如下类型,数组元素中的1代表两边和底边,代表方块移动到此处后便不能再移动。
- 七种具体的小方块用4*4的二维数组表示,当按下上的变换按键之后就会按如下方式变换(其他类似):
-
判断下降到底或者撞墙,是通过不断计算4*4的二维数组和二维数组所在的maps数组中对应位置的数字之和,如果相加之后有值为2,则肯定有方块重叠了,视为重合。
int Tetris::Judge_Hit_Wall(int tempArray[][4])//判断是否撞到墙 { for (int i=widthOfLocation; i<4+widthOfLocation; i++) { for (int j=heightOfLocation; j<4+heightOfLocation; j++) { //如果现在移动的俄罗斯方块和maps数组重元素相加为2,则表示重合,那么返回1 if (tempArray[j-heightOfLocation][i-widthOfLocation] + maps[j][i] == 2) return 1; } } return 0; }
-
另外maps定义成了20*12的数组,主要是表格上面预留了三行,要达到的效果就是让方块从上面缓缓降下来,然后通过表格上一行判断是否有1,来判断游戏是否结束(图很难看,凑活着看吧)。
-
总共19种方块:
int gra[19][4][4] = { {0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0} };
- 通过以下两个变量来定位二维4*4数组在maps中的位置
然后俄罗斯方块游戏的流程:
-
先看主要流程。
void Widget::dealSignal() { tetris.Deal_Key();//按键处理 tetris.Tetris_Move();//方块向下移动一步 tetris.Line_Full();//判断一行是否满 tetris.setKey(0); if (tetris.GameOver())//游戏结束自动按下停止按键 on_buttonStop_clicked(); ui->lcdNumber->display( tetris.getScore() );//显示分数 update();//刷新 }
-
Deal_Key(),先判断按键是否按下,如果按下,执行相应的操作。
key = 1 变换方块
key = 2 延时变短,加速下降(这块程序没写,有时间再写吧)
key = 3 向左移动一步
key = 4 想右移动一步
void Tetris::Deal_Key(void)//处理按键函数 { if (key == 1) { switch ( index ) { case 0: Change_Array( gra[++index] );//先将俄罗斯方块的索引值改变,然后通过索引值改变方块的形状 break; case 1: Change_Array( gra[--index] ); break; case 2: Change_Array( gra[++index] ); break; case 3: Change_Array( gra[--index] ); break; case 4: Change_Array( gra[++index] ); break; case 5: Change_Array( gra[--index] ); break; case 6: break; case 7: Change_Array( gra[++index] ); break; case 8: Change_Array( gra[++index] ); break; case 9: Change_Array( gra[++index] ); break; case 10: index -= 3; Change_Array( gra[index] ); break; case 11: Change_Array( gra[++index] ); break; case 12: Change_Array( gra[++index] ); break; case 13: Change_Array( gra[++index] ); break; case 14: index -= 3; Change_Array( gra[index] ); break; case 15: Change_Array( gra[++index] ); break; case 16: Change_Array( gra[++index] ); break; case 17: Change_Array( gra[++index] ); break; case 18: index -= 3; Change_Array( gra[index] ); break; default: break; } } if (key == 2)//延时变短,加速下降 { } if (key == 3)//向左移动 { widthOfLocation--; if ( Judge_Hit_Wall(array) )//如果有重合,就不移动 { widthOfLocation++; } } if (key == 4)//向右移动 { widthOfLocation++; if ( Judge_Hit_Wall(array) )//如果有重合,就不移动 { widthOfLocation--; } } }
Change_Array(此函数是更新二维的4*4数组,按下变换的按键后,需要将变换之后的数组更新到移动的数组中,从而达到更新显示方块的目的)。
当然更新数组之前,需要先判断更新数组之后,会不会出现和已有的方块重合的情况,如果重合,那么就不变换,直接退出。
void Tetris::Change_Array(int temp_maps[4][4])//复制数组 { if ( Judge_Hit_Wall(temp_maps) )//变换之后判断是否撞墙,如果撞墙就不变换 return; for (int i=0; i<4; i++)//把按下按键之后变换的方块的数组坐标赋值到正在下降的数组坐标中 for (int j=0; j<4; j++) array[i][j] = temp_maps[i][j]; return ; }
-
Tetris_Move(),方块向下移动一步(这个过程比较复杂,需要拿着纸笔一点一点的耐心计算),下降过程中如果碰到底部,就不再下降。
void Tetris::Tetris_Move() { heightOfLocation++;//方块下降一格 for (int i=widthOfLocation; i<4+widthOfLocation; i++) { for (int j=heightOfLocation; j<4+heightOfLocation; j++) { //下降过程中,循环比对16个方块,如果有等于2的,就表示已经下降到底部 if (array[j-heightOfLocation][i-widthOfLocation] + maps[j][i] == 2) { heightOfLocation--;//heightOfLocation-- 把降下来的一个在升回去 //for循环,将现在的方块坐标复制到maps数组中 for (int i=widthOfLocation; i<4+widthOfLocation; i++) { for (int j=heightOfLocation; j<4+heightOfLocation; j++) { if (array[j-heightOfLocation][i-widthOfLocation] == 1) { maps[j][i] = 1; } } } for (int i=0; i<20; i++) { for (int j=0; j<12; j++) { mapTemp[i][j] = maps[i][j]; } } heightOfLocation = 0;//一系列初始化 widthOfLocation = 4; index = indexNext;//随机生成俄罗斯方块; indexNext = random(19); key = 0;//方块下降到底部之后,按键值初始化为零 Change_Array( gra[index] );//通过索引值将数组赋值到下降的数组中 qDebug() << "index = " << index; return ;//下降到底部,终止函数 } if (array[j-heightOfLocation][i-widthOfLocation] == 1)//显示正在下落的方块 mapTemp[j][i] = 1; } } }
-
Line_Full(),判断每行是否满,如果满就消行。
void Tetris::Line_Full(void) { static int Line = 0; for (int j=18; j>0; j--)//十四行 { int sum = 0; for (int i=1; i<=10; i++)//判断每一行是否都为1 sum += maps[j][i];//10个数组元素的和 if (sum == 10)// { Line++; int k = j; for (; k>0; k--)//从满的那一行开始通过循环将以上的方格数据全部复制下来 for (int i=1; i<=10; i++) maps[k][i] = maps[k-1][i]; if (k == 0)//最上面一行全部赋值为零 for (int i=1; i<=10; i++) maps[k][i] = 0; j++; } } switch(Line) { case 1: score += 10; break; case 2: score += 30; break; case 3: score += 50; break; case 4: score += 100; break; default: break; } Line = 0; return ;//函数终止 }
-
判断游戏是否结束。
bool Tetris::GameOver(void) { for (int n=1; n<=10; n++) if (maps[2][n] == 1) { qDebug() << "Game Over"; return 1; } return 0; }
-
更新分数,更新显示。
ui->lcdNumber->display( tetris.getScore() );//显示分数 update();//刷新
游戏界面:
欢迎交流
上一篇: 【成长】程序员的成长学习笔记(长更)
下一篇: Python学习笔记_5基本数据类型