C++课程设计实验报告俄罗斯方块.docx
《C++课程设计实验报告俄罗斯方块.docx》由会员分享,可在线阅读,更多相关《C++课程设计实验报告俄罗斯方块.docx(25页珍藏版)》请在冰豆网上搜索。
C++课程设计实验报告俄罗斯方块
一、需求分析
1.1系统概述
该游戏在DOS下为玩家提供传统俄罗斯方块游戏的基本功能,玩家可以通过键盘控制在游戏区中堆积软件随机提供的由四个小方块不同组合的7种类型不同颜色的方块,每个小方格的颜色也是随机的,并且在整个下落的过程中,其颜色也呈动态变化。
游戏过程中,每在游戏区堆满一行后,自动消除并记分。
同时消去的行数越多,那一次性所加的分数也就越多。
一行是100,两行是200,三行是400,四行是500分。
为了得到更多的分,那么我们游戏者就要想办法一次尽可能多的消去方块。
当游戏区不能再堆积新来的方块时,游戏结束。
游戏设定3个级别,初级,中级和高级,每个级别有分三个小的级别,级别越高,方块下降的速度越快、难度越大。
为了避免游戏频发枯燥,增加游戏的趣味性,该游戏为游戏者插入了音乐,对该功能有实现暂停的控制。
该游戏的以“英雄榜”来判断玩家水平的高低,如果玩家的得分大于了保存的最高分,则将玩家的的得分写入文件,如果得分不大于最高分,则保持不变。
游戏以最终玩家获得的分数来判断玩家水平的高低。
1.2功能需求描述
这次实验以及部分功能的实现都是一次小小的尝试,获得不错的效果。
这个游戏,不仅可以满足游戏爱好者对游戏的要求,同时我们增加了趣味性,让游戏有一个比较好听的背景音乐,在玩游戏的同时,让玩家饱享视听大宴。
这有别于常规的俄罗斯方块算法,游戏中,玩家依靠自己消层来得分,而且保证了玩家对游戏趣味性的追求,并且游戏的英雄榜功能为玩家提供了一个良好的测试水平的平台。
当玩家游戏池中的砖块累积到顶端时游戏失败。
二、系统设计
2.1数据流程图
数据流图是对系统数据流向的一种描述,并从本质上让程序的使用者,大致了解系统的使用方法。
本俄罗斯游戏的大致流程图如下:
2.2程序功能模块
2.3程序流程图
三、关键代码描述
3.1程序模块详细设计
3.1.1界面初始化
程序界面程序在启动运行时,系统会通过调用视图类中的重绘图函数对界面进行界面的初始化。
其界面如图所示:
实现该功能的代码如下:
voidViewWindows:
:
draw()
{
initgraph(640,480);
srand((unsigned)time(NULL));
//显示操作说明
setfont(14,0,_T("宋体"));
outtextxy(20,330,_T("操作说明"));
outtextxy(20,350,_T("上:
旋转"));
outtextxy(20,370,_T("左:
左移"));
outtextxy(20,390,_T("右:
右移"));
outtextxy(20,410,_T("下:
下移"));
outtextxy(20,430,_T("空格:
沉底"));
outtextxy(20,450,_T("ESC:
退出"));
//设置坐标原点
setorigin(220,20);
//绘制游戏区边界
rectangle(-1,-1,WIDTH*SIZE,HEIGHT*SIZE);
rectangle((WIDTH+1)*SIZE-1,-1,(WIDTH+5)*SIZE,4*SIZE);
}
voidViewGameinfo:
:
draw(intScore,intLevel,intHigest_score)
{
charsc[10],sl[10],sr[10];
intHigest_num;
ifstreamfile_in("d:
\\Higest_score.txt");
if(!
file_in)
{
ofstreamfile_ou("d:
\\Higest_score.txt");
file_ou<<0<return;
}
file_in>>Higest_num;
Higest_score=Higest_num;
sprintf(sc,"%4d",Score);
sprintf(sl,"%4d",Level);
sprintf(sr,"%4d",Higest_score);
outtextxy(-200,100,_T("Score"));
outtextxy(-200,120,sc);
outtextxy(-200,140,_T("Level"));
outtextxy(-200,160,sl);
outtextxy(-200,180,_T("Higest_score"));
outtextxy(-200,200,sr);
}
voidViewBlock:
:
DrawBlock(BLOCKINFO_block,DRAW_draw)
{
WORDb=g_Blocks[_block.getID()].dir[_block.getDir()];
intx,y;
intcolor=BLACK;
switch(_draw)
{
caseSHOW:
color=g_Blocks[_block.getID()].color;break;
caseHIDE:
color=BLACK;break;
caseFIX:
color=g_Blocks[_block.getID()].color/3;break;
}
setfillstyle(color);
for(inti=0;i<16;i++)
{
if(b&0x8000)
{
x=_block.getX()+i%4;
y=_block.getY()-i/4;
if(y{
if(_draw!
=HIDE)
bar3d(x*SIZE+2,(HEIGHT-y-1)*SIZE+2,(x+1)*SIZE-4,(HEIGHT-y)*SIZE-4,3,true);
else
bar(x*SIZE,(HEIGHT-y-1)*SIZE,(x+1)*SIZE-1,(HEIGHT-y)*SIZE-1);
}
}
b<<=1;
}
}
3.1.2游戏随机获取新方块的实现如下:
voidControl:
:
NewGame()
{
//清空游戏区
setfillstyle(BLACK);
bar(0,0,WIDTH*SIZE-1,HEIGHT*SIZE-1);
ZeroMemory(g_World,WIDTH*HEIGHT);
Level=0;
ViewGameinfov;
v.draw(Score,Level,Higest_score);
g_NextBlock.setID(rand()%9);
g_NextBlock.setDir(rand()%4);
//获取新方块
NewBlock();
}
voidControl:
:
NewBlock()
{
//生成下一个方块
g_NextBlock.setX(WIDTH+1);
g_NextBlock.setY(HEIGHT-1);
g_CurBlock.setID(g_NextBlock.getID()),g_NextBlock.setID(rand()%9);
g_CurBlock.setDir(g_NextBlock.getDir()),g_NextBlock.setDir(rand()%4);
g_CurBlock.setX((WIDTH-4)/2);
g_CurBlock.setY(HEIGHT+2);
//下移新方块直到有局部显示
WORDc=g_Blocks[g_CurBlock.getID()].dir[g_CurBlock.getDir()];
while((c&0xF)==0)
{
g_CurBlock.setY(g_CurBlock.getY()-1);
c>>=4;
}
//绘制新方块
ViewBlockv;
v.DrawBlock(g_CurBlock);
//绘制下一个方块
setfillstyle(BLACK);
bar((WIDTH+1)*SIZE,0,(WIDTH+5)*SIZE-1,4*SIZE-1);
v.DrawBlock(g_NextBlock);
}
3.1.3游戏接收命令的实现如下:
CTRLControl:
:
GetControl(intLevel,bool_onlyresettimer)
{
intflag;
switch(Rank)
{
case1:
flag=800;break;
case2:
flag=400;break;
case3:
flag=200;break;
default:
flag=800;
}
staticDWORDoldtime=GetTickCount();
//重置计时器
if(_onlyresettimer)
{
oldtime=GetTickCount();
returnCTRL_DOWN;//仅仅为了重置计时器,随便返回一个值
}
flag=flag/(Level+Rank);
//获取控制值
while(true)
{
//如果超时,自动下落一格
DWORDnewtime=GetTickCount();
if(newtime-oldtime>=flag)
{
oldtime=newtime;
returnCTRL_DOWN;
}
//如果有按键,返回按键对应的功能
if(kbhit())
{
switch(getch())
{
case'w':
case'W':
returnCTRL_ROTATE;
case'a':
case'A':
returnCTRL_LEFT;
case'd':
case'D':
returnCTRL_RIGHT;
case's':
case'S':
returnCTRL_DOWN;
case'p':
case'P':
returnCTRL_PAUSE;
case27:
returnCTRL_QUIT;
case'':
returnCTRL_SINK;
case0:
case0xE0:
switch(getch())
{
case72:
returnCTRL_ROTATE;
case75:
returnCTRL_LEFT;
case77:
returnCTRL_RIGHT;
case80:
returnCTRL_DOWN;
}
}
}
}
}
3.1.4程序分配命令的实现如下:
//分发控制命令
voidGAME:
:
DispatchControl(CTRL_ctrl)
{
switch(_ctrl)
{
caseCTRL_ROTATE:
g_CurBlock.OnRotate();break;
caseCTRL_LEFT:
g_CurBlock.OnLeft();break;
caseCTRL_RIGHT:
g_CurBlock.OnRight();break;
caseCTRL_DOWN:
OnDown();break;
caseCTRL_SINK:
OnSink();break;
caseCTRL_PAUSE:
Gamepause();break;
caseCTRL_QUIT:
break;
}
}
3.1.5方块左右移动和旋转的实现如下:
voidBLOCKINFO:
:
OnLeft()
{
BLOCKINFOtmp=*this;
tmp.setX(tmp.getX()-1);
if(CheckBlock(tmp))
{
v1.DrawBlock(*this,HIDE);
x--;
v1.DrawBlock(*this);
}
}
voidBLOCKINFO:
:
OnRight()
{
BLOCKINFOtmp=*this;
tmp.setX(tmp.getX()+1);
if(CheckBlock(tmp))
{
v1.DrawBlock(*this,HIDE);
x++;
v1.DrawBlock(*this);
}
}
voidBLOCKINFO:
:
OnRotate()
{
//获取可以旋转的x偏移量
intdx;
BLOCKINFOtmp=*this;
tmp.setDir(tmp.getDir()+1);
if(CheckBlock(tmp))
{
dx=0;gotorotate;
}
tmp.setX(x-1);if(CheckBlock(tmp))
{
dx=-1;gotorotate;
}
tmp.setX(x+1);if(CheckBlock(tmp))
{
dx=1;gotorotate;
}
tmp.setX(x-2);if(CheckBlock(tmp))
{
dx=-2;gotorotate;
}
tmp.setX(x+2);if(CheckBlock(tmp))
{
dx=2;gotorotate;
}
return;
rotate:
//旋转
v1.DrawBlock(*this,HIDE);
dir++;
x+=dx;
v1.DrawBlock(*this);
}
3.1.6方块下落和沉底的实现如下:
voidGAME:
:
OnDown()
{
ViewBlockv;
BLOCKINFOtmp=g_CurBlock;
tmp.setY(tmp.getY()-1);
if(g_CurBlock.CheckBlock(tmp))
{
v.DrawBlock(g_CurBlock,HIDE);
g_CurBlock.setY(g_CurBlock.getY()-1);
v.DrawBlock(g_CurBlock);
}
else
OnSink();//不可下移时,执行“沉底方块”操作
}
voidGAME:
:
OnSink()
{
inti,x,y;
intcount=0;
ViewBlockv1;
ViewGameinfov2;
//连续下移方块
v1.DrawBlock(g_CurBlock,HIDE);
BLOCKINFOtmp=g_CurBlock;
tmp.setY(tmp.getY()-1);
while(g_CurBlock.CheckBlock(tmp))
{
g_CurBlock.setY(g_CurBlock.getY()-1);
tmp.setY(tmp.getY()-1);
}
v1.DrawBlock(g_CurBlock,FIX);
//固定方块在游戏区
WORDb=g_Blocks[g_CurBlock.getID()].dir[g_CurBlock.getDir()];
for(i=0;i<16;i++)
{
if(b&0x8000)
{
if(g_CurBlock.getY()-i/4>=HEIGHT)
{//如果方块的固定位置超出高度,结束游戏
Gameover();
return;
}
else
g_World[g_CurBlock.getX()+i%4][g_CurBlock.getY()-i/4]=1;
}
b<<=1;
}
//检查是否需要消掉行,并标记
introw[4]={0};
boolbRow=false;
for(y=g_CurBlock.getY();y>=max(g_CurBlock.getY()-3,0);y--)
{
i=0;
for(x=0;xif(g_World[x][y]==1)
i++;
if(i==WIDTH)
{
bRow=true;
count++;
row[g_CurBlock.getY()-y]=1;
setfillstyle(WHITE,DIAGCROSS2_FILL);
bar(0,(HEIGHT-y-1)*SIZE+SIZE/2-2,WIDTH*SIZE-1,(HEIGHT-y-1)*SIZE+SIZE/2+2);
}
}
if(bRow)
{
//延时200毫秒
Sleep(200);
//擦掉刚才标记的行
IMAGEimg;
for(i=0;i<4;i++)
{
if(row[i])
{
for(y=g_CurBlock.getY()-i+1;yfor(x=0;x{
g_World[x][y-1]=g_World[x][y];
g_World[x][y]=0;
}
getimage(&img,0,0,WIDTH*SIZE,(HEIGHT-(g_CurBlock.getY()-i+1))*SIZE);
putimage(0,SIZE,&img);
}
}
if(count==4)
{
count=5;
}
if(count==3)
{
count=4;
}
Score+=100*count;
Level=(Score-(Rank-1)*1500)/500;
if(Level>=3)
{
Rank++;
if(Rank>3)
{
HWNDwnd=GetHWnd();
if(MessageBox(wnd,_T("恭喜你,你已经征服该游戏!
"),_T("提醒"),MB_OK))
Quit();
}
else
{
HWNDwnd=GetHWnd();
if(MessageBox(wnd,_T("恭喜你,进入下一关!
"),_T("提醒"),MB_OKCANCEL|MB_ICONQUESTION)==IDOK)
NewGame();
else
{
Quit();
}
}
}
v2.draw(Score,Level,Higest_score);
}
//重新计算延时
NewBlock();
GetControl(Level,true);
}
消行瞬间
3.1.7结束和退出实现如下:
voidControl:
:
Gameover()
{
HWNDwnd=GetHWnd();
ofstreamfile_out("d:
\\Higest_score.txt");
if(!
file_out)
return;
if(Higest_score<=Score)
{
file_out<}
file_out.close();
if(MessageBox(wnd,_T("游戏结束。
\n您想重新来一局吗?
"),_T("游戏结束"),MB_YESNO|MB_ICONQUESTION)==IDYES)
{
Rank=1;
Score=0;
NewGame();
}
else
Quit();
}
//退出游戏
voidControl:
:
Quit()
{
closegraph();
exit(0);
}
3.2附加功能的实现
3.2.1计分功能模块:
1).classControl
{
public:
voidNewGame();
voidNewBlock();
CTRLGetControl(intLevel,bool_onlyresettimer=false);
voidGameover();
voidQuit();
protected:
BLOCKINFOg_CurBlock,g_NextBlock;
staticintScore;
staticintLevel;
intRank;
在Control类中,定义了Score,Level,Rank几个静态的成员变量,并且给它们进行初始化操作。
intGAME:
:
Score=0;
intGAME:
:
Level=0;
2).对计分方式的具体的操作。
(1)intflag;
switch(Rank)
{
case1:
flag=800;break;
case2:
flag=400;break;
case3:
flag=200;break;
default:
flag=800;
}
flag=flag/(Level+Rank);
(2).Score+=100*count;
Level=(Score-(Rank-1)*1500)/500;
if(Lev