C++语言实现俄罗斯方块经典游戏课程设计.docx
《C++语言实现俄罗斯方块经典游戏课程设计.docx》由会员分享,可在线阅读,更多相关《C++语言实现俄罗斯方块经典游戏课程设计.docx(12页珍藏版)》请在冰豆网上搜索。
![C++语言实现俄罗斯方块经典游戏课程设计.docx](https://file1.bdocx.com/fileroot1/2022-12/13/d80e064a-c493-4353-b25b-6c8625c913ca/d80e064a-c493-4353-b25b-6c8625c913ca1.gif)
C++语言实现俄罗斯方块经典游戏课程设计
计算机实习报告
一.功能说明
1.1总体功能说明
本工程用C++语言实现了俄罗斯方块经典游戏。
俄罗斯方块游戏特点:
俄罗斯方块的基本规则是通过键盘控制移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分,上手简单,是一款经典游戏。
在经典玩法(正常模式)的基础上,本程序还添加了挑战模式,即在游戏伊始时在游戏区域中随机分布方块,减小了留给玩家调整方块的时间和区域,需要通过玩家不断填补消行才能把游戏进行下去。
游戏会随着玩家的得分的增多而加大方块下落的速度,从而增加游戏难度,因此也在原来的基础上大大增加了游戏的可玩性。
游戏中还增加了Esc键的功能,从而可以实现暂停游戏和退出游戏的功能。
1.2用户界面
这是游戏的初始界面。
出现游戏名,根据提示,玩家按下“c”“n”即可自行选择挑战模式和正常模式,难度不同。
这样既明确了游戏名称,提供两种模式的入口,又给玩家一个准备的时间以自行控制游戏开始,不必一打开文件就突然开始游戏,增加了游戏的人性化。
以上两图为进入游戏后的主界面,分别为正常模式和挑战模式。
中间的区域为游戏主要界面,游戏从上往下随机产生并自动输出七种方块,对俄罗斯方块的基本操作如←(左移),→(右移),↑(方块变形),↓(加速下落)都在此区域里完成。
最上方显示的是模式。
右边的区域显示一些与游戏相关的信息,从上往下分别是下落速度、游戏得分、游戏操作说明,编者信息。
此图为按下“Esc”键后出现的画面。
程序支持Esc键暂停的功能,即按下后,按Y键退出,按N键可继续游戏,玩家中途有事离开,回来后也可继续游戏,提供了方便。
1.3使用方法
打开程序后根据屏幕上的提示信息,按下回车键以进入游戏界面开始游戏。
进入游戏界面后,根据屏幕右下方的游戏说明,通过键盘上←(左移),→(右移),↑(方块变形),↓(加速下落)控制方块。
游戏过程中,可按“Esc”键暂停,并可以通过“Y”和“C”键选择退出和继续。
游戏结束之后,通过键盘按键“Y”和“N”选择是否退出游戏。
←,→,↑,↓:
左移,右移,翻转和加速下落。
Esc:
暂停/继续/退出
游戏难度配置是随着分数的增加而提高的。
二.程序设计说明
2.1总体设计框架
程序以main函数为主线,在main函数中调用其他函数,具体算法主要在其他函数中实现,使程序更加简洁明了,清晰而有层次。
除main函数外,还有Start,Begin,Block_birth,Print,Clear,Turn,Intomap,Available,Deleteline,Gameover十个实现算法的函数,分别用来实现初始界面,初始游戏,生成方块,打印方块,擦除方块,旋转方块,更改地图矩阵,判断是否放下,消行,游戏结束的功能。
框架图如下:
Y
N
N
Y
N
Y
N
Y
2.2关键算法描述
本工程主要采用了数组来存储矩阵,用三维数组(block_shape[7][4][4])存储了最基本的7种俄罗斯方块,而在具体操作中则通过对另外两个新定义的二维数组当前方块(tshape[4][4])与新方块(newshape[4][4])来实现各种操作与判断。
打印函数Print()和擦除函数Clear()可实现方块的移动。
判断函数Available()进行方块满行和叠加的判断,主要是通过定义了一个二维的地图矩阵map_shape[24][46],在有方块的地方值为1,无则为0,对于“■”字符横坐标占两个的问题,统一取其左边第一个位置为1进行各种判断操作。
关键算法1:
算法功能:
在主程序中接收键盘传来的信息,并对所要操纵的方块做相应的操作(翻转,左移,右移,下落),包括Esc键控制的暂停/继续/退出游戏。
voidTurn(intblock_shape[4][4])//操作方块
{
intdirection=_getch();
switch(direction)
{
case72:
//up方块翻转
for(intk=0;k<4;k++)
{
for(intj=0;j<4;j++)
{
block_newshape[3-j][k]=block_shape[k][j];
}
}
if(Available(block_newshape,0,0))
{
Clear(block_tshape);
Print(block_newshape);
for(intk=0;k<4;k++)
for(intj=0;j<4;j++)
{block_tshape[k][j]=block_newshape[k][j];}
}
break;
case75:
//left方块左移
if(Available(block_tshape,-2,0))
{
Clear(block_tshape);
x_coordinate-=2;
Print(block_tshape);
}
break;
case77:
//right方块右移
if(Available(block_tshape,2,0))
{
Clear(block_tshape);
x_coordinate+=2;
Print(block_tshape);
}
break;
case80:
//down方块下移
delay=delay_max;
break;
case27:
//Esc暂停游戏
textout(handle,23,9,BColors,1,"Doyouwanttoquit?
");
textout(handle,24,10,BColors,1,"pressytoexit");
textout(handle,24,11,BColors,1,"pressntocontinue");
inttemp=_getch();
switch(temp)
{
case121:
//Y退出游戏
textout(handle,24,9,Colors,1,"");
textout(handle,24,10,Colors,1,"");
textout(handle,24,11,Colors,1,"");
textout(handle,28,12,BColors,1,"score:
");
charoutput[10];//临时输出矩阵
itoa(score,output,10);
textout(handle,35,12,BColors,1,output);
PlaySound("dead.wav",NULL,SND_FILENAME|SND_ASYNC);
exit(0);
case110:
//N继续游戏
textout(handle,24,9,Colors,1,"");
textout(handle,24,10,Colors,1,"");
textout(handle,24,11,Colors,1,"");
break;
default:
//输入错误提示
textout(handle,50,10,Colors,1,"inputerror...");
textout(handle,50,11,Colors,1,"ifyouwanttoexit,");
textout(handle,50,12,Colors,1,"pleasepressEscagain");
Sleep(1000);
textout(handle,50,10,Colors,1,"");
textout(handle,50,11,Colors,1,"");
textout(handle,50,12,Colors,1,"");
}
break;
}
}
关键算法2:
算法功能:
判断方块能否出现在某个位置。
boolAvailable(intblock_shape[4][4],intx_move,inty_move)//判断方块能否放下
{
for(intx=0;x<4;x++)
{
for(inty=0;y<4;y++)
{
if(block_shape[x][y]==1)
{
switch(y)
{
case0:
if(map_shape[y_coordinate+y_move+x][x_coordinate+x_move+y]&&block_shape[x][y])
return0;
break;
case1:
case2:
case3:
if(map_shape[y_coordinate+y_move+x][x_coordinate+x_move+2*y]&&block_shape[x][y])
return0;
break;
}
}
}
}
return1;
}
关键算法3;
算法功能:
消除已经填满的一行。
voidDeleteline()//擦除一行方块
{
for(inty=0;y<4;y++)
{
for(intx=0;x<4;x++)
{
if(block_tshape[y][x]==1)
if(Block_Full(y))//判断是否满行
{textout(handle,30,7,GColors,1,"消行成功!
");
Sleep(500);
textout(handle,30,7,GColors,1,"");//显示“消行成功”
cout<<'\a';//消行时蜂鸣
score+=10;//加分
if(speed<=49)//更改速度
{
speed=score/10;
if(speed>49)
{
speed=49;
delay_max=2;
}
else
delay_max=100-speed*2;
}
charoutput[10];//临时输出矩阵
itoa(score,output,10);
textout(handle,58,9,BColors,1,output);
itoa(speed,output,10);
textout(handle,58,5,BColors,1,output);
for(inta=y_coordinate+y;a>3;a--)
{
for(intb=21;b<45;b+=2)
map_shape[a][b]=map_shape[a-1][b];
}//从消行处开始下移地图矩阵
for(intx=y_coordinate+y;x>3;x--)
{
for(inty=21;y<45;y+=2)
{
textout(handle,y,x,Colors,1,"");
if(map_shape[x][y]==1)
textout(handle,y,x,Colors,1,"■");
}
}//从消行处开始向上重新打印
}
}
}
}
2.3程序设计的难点和关键点
第一
对于方块的位置的判断。
因为在方块做任何操作之前都要进行判断,看变化之后的方块所处的位置具不具备出现方块的条件,即是否该坐标位置是否已经被占用。
因此一个能够通用的判断函数就非常关键,在本程序中函数boolAvailable()就发挥着这个作用。
第二
对于方块的操作。
向左向右相对来说比较好解决,只需要修改横坐标即可,而向上键则需要实现方块的反转,这需要用到一个类似矩阵转置的算法。
向下的实现引入了延迟量,即相当每一层横坐标的循环中给玩家一个操作时间,当玩家按“↓”就相当于放弃调整进入下一个横坐标的循环。
第三
对于消行的判断与操作。
因为“■”字符横坐标占两个的问题,导致打印方块时横坐标都要乘以2,于是统一取左边第一个位置为1进行各种操作。
故最终判断是否满行时该行的地图矩阵存储的数据是1、0交错的。
在判断消行时因为每次消行判断只与当前操作的方块有关,所以判断只需在有方块的横坐标处进行判断,再者消行时也只从开始消行的横坐标向上操作,一下部分不需要变动,可以增加程序执行的效率。
2.4调试的方法
问题1:
“■”字符横向输出占两格问题
同样是坐标,因为之前下手写比较简单的打印和擦除函数实现方块移动的时候,没有细想就用横坐标乘以2的方式把方块画出来了,到了后期编判断函数的时候一直为此烦恼,后来索性把打印的“■”换成“10”观察数据应该如何存储,而得出向左靠齐存储的想法。
问题2:
判断函数问题
因为要编一个通用的判断函数,所以一直向左向右判断与向下判断与原地判断就要都写在一个函数里,再加上横向输出占两格的问题,一度无法实现,不过通过不断运行调试,纵然有很多错误之处,但每发现一个错误就会都修改程序消除这个错误,但是其他错误并没有解决,但当你针对每一个错误解决完的时候,也就是编出了正确代码的时候。
问题4:
itoa函数的调试过程
分数显示时需用调用了这个函数。
经过多次调试,发现其初始是默认初始,便把初始置0,后来运行程序分数便能够正确累加。
问题3:
语法错误
程序刚编写的时候有许多语法错误,后经过不断地设断点调试,以及分模块的调试,逐步修正,完成了整个程序。
2.5程序性能评价
在程序运行的效果上还是非常流畅的,不论是方块自身的反转变换移动,还是满行后的消行都没有延迟,在实现键盘操纵的过程中也没有什么延迟感。
在程序设计上大体上感觉还是不错的,通过几个函数模块互相嵌套调用实现程序设计上的几个模块的功能,减少了代码重复率,增加了代码执行的效率。
在程序的精简度方面做的还不够,由于许多函数内都重复调用了一些变量,导致可能设置了较多的全局变量。
另外方块并没有实现彩色随机出现,而都为红色。
且在界面右边也未设计出现下一个方块的函数,增大了游戏的难度。
但基于此程序中方块生成及打印的算法,这两点恐怕无法实现,在这一点上做得不够好。
三.心得体会
通过这次小学期的计算机实习,我的编程能力的得到了很大提升。
通过实践将原来学习的许多理论知识应用到编程实际中,不仅巩固了自己的与原来已经掌握的知识,还锻炼了自己的动手能力,增强了信心。
之前正常课时的C++学习只是基于书本,平时的上机也只局限于对一些小程序的调试和部分语句的编写,表明上虽然懂了,但是离实践还有很远。
我们真正消化为自己掌握的内容其实很少,所以对于C++,只是个平面的印象,然而这次计算机实习让它变得立体鲜活。
一开始的我还毫无头绪,但是通过老师的讲解,我首先从网上查找了一些代码,自己试着调试,尝试先看懂每个函数的功能,大致了解编写俄罗斯方块的基本思路,再自己尝试着一边借鉴一边原创。
由于是自己编写的程序,所以开始运行时出现了很多错误,由此我对程序的调试过程有了更加深刻的理解,并能熟练地运用这些调试方法。
某些错误是语法的错误,有的是自己的不仔细造成的错误,这些错误都使我对于C++的编程以及自己的细心程度有了极大的提升。
通过这次计算机实习,通过俄罗斯方块这个小游戏的编写,我接触了不少不曾用过的函数,了解了控制台编程,体会到模块化,多文件,多线程的好处。
这次实习让我在编译原理方面得到了巩固,而且使我们对软件的编写有了一个初步的认识,同时,我的动手能力得到了提高。
总之,这次实验让我受益匪浅。