c语言俄罗斯方块实验报告.docx
《c语言俄罗斯方块实验报告.docx》由会员分享,可在线阅读,更多相关《c语言俄罗斯方块实验报告.docx(21页珍藏版)》请在冰豆网上搜索。
c语言俄罗斯方块实验报告
C语言之游戏
俄罗斯方块课程设计报告
专业:
[]
学生姓名:
[]
指导教师:
[]
完成时间:
一、需求分析
1.该程序是完成一个简易的俄罗斯方块的任务,其要完成几个重要的功能:
界面,方块下落,旋转,判断是否还能下落,左右移动,分数,速度设置,清楚满的每行,下个方块的预览等;
2.可用#include<>的头文件来实用几个函数来控制并完成游戏的界面;
3.可用7个二维5*5数组去实现下落方块的全部类型,再用随机函数使其随机下落;
4.用#include<>的头文件去使用按键的控制,以保证用户能够合理操作;并用#include<>的头文件来使用rand函数来随机出示七种方块;完成正常的显示和下个方块的预览;
5.用#include<>来运用时针去控制时间;
6.程序执行过程:
然后加上几个基本的头文件来执行函数,首先设置按键操作,用:
w,a,d,s控制,然后进行界面的初始化设置,启动,时针参数设置,开始新游戏,开始随机下落方块,显示下一个方块,再行旋转,移动,清除,加分,速度参数,最后判断是否已满,停止游戏,打出分数,结束的画面设置,游戏终止...
二、概要设计
该程序中的函数设置总体主要功能大致如下
1.找到合适的方块盒子(7种)使其一一下落box[MAX_C][5][5]={/*MAX_C(7)种预定义的盒子*/这样用类似的7个5*5二维数组来实现每个盒子方块的形状,自此就引荐两个作为介绍,{
{0,0,0,0,0},
{0,0,0,0,0},
{0,0,1,0,0},
{0,1,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{1,1,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
},如此中用1代表显示的格子,0代表空。
2.设置时间指针,实现旋转,移动,消去,显示加分,改变速度分别用下面函数表示
intsetTimer(Timer*t,unsignedintintv,BOOLen);
/*设置时钟t,参数分别为时钟指针,时间间隔,是否活动*/
voidrotateBox(intbox1[5][5],intbox2[5][5])
/*旋转box1输出到box2*/
intmove(intdir)/*实现移动,返回成功与否*/
voidclear()/*清除掉满行*/
voidprscore()/*打印现在的分数*/
voidspe()/*显示速度的改变*/
3.外部函数功能如下图
4.建立整个函数的流程图如下:
N
Y
Y
N
游戏结束
流程简介:
玩家定义游戏开始,游戏显示开始界面,时针已经设定好,然后随机出示方块,玩家进行方块的旋转,左右移动,和下落控制,然后在旁边的表格中出示另一个表格显示下一个即将下落的方块,然后判断是否到达底部,若到,继续出示方块,若没,则可继续变换,下落到低端后在进行判断,是否可以消除该行,再进行分数的变换,速度的调整,然后判断是否已经满,若未满,则继续出示下一个方块,若满,则结束游戏,打印分数,出示结束界面,游戏终止。
三、详细设计
1.设置七种方块:
这个用intbox[MAX_C][5][5]=/*MAX_C(7)种预定义的盒子*/来实现;即七个5*5的盒子数组,方块实体用1表示,0表示空;七种如下所示,细心就会发现{
{0,0,0,0,0},
{0,0,0,0,0},
{1,1,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,0,1,0,0},
{0,1,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,1,1,0,0},
{0,0,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,0,1,1,0},
{0,1,1,0,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,1,1,0,0},
{0,0,1,0,0},
{0,0,1,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,0,1,1,0},
{0,0,1,0,0},
{0,0,1,0,0},
{0,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,0,0}
}
};这样七种盒子的方块就被清楚的展现在你们面前。
用来保证方块不会出来
2.我们要进行按键的设置#defineKEY_UP'w'/*定义上下左右按按键*/
#defineKEY_DOWN's'
#defineKEY_LEFT'a'
#defineKEY_RIGHT'd'
#defineKEY_ESC27/*退出*/
3.时钟的控制,用来控制方块的下落间隔时间及速度/*时钟结构控制*/
typedefstruct{/*时钟结构*/
BOOLenabled;/*时钟是否开启*/
unsignedintintervel;/*定时间隔*/
unsignedintlasttime;/*这个属于内部使用变量*/
}Timer;
4.intGetTickCount(){/*读取BIOS时钟*/
intret;
ret=peek(0x0,0x46e);/*实际上读取了内存0:
046e处的内容*/
ret<<=8;/*这个地方是$%#$^$%&^*/
ret+=peek(0x0,0x46c);/*太多新的东西了,找点书看一看吧*/
return(ret);
}
intsetTimer(Timer*t,unsignedintintv,BOOLen)
{
t->enabled=en;/*设置一个时钟罗*/
t->intervel=intv;
t->lasttime=GetTickCount();/*lasttime记录的是上一个*/
/*tickcount返回的东西*/
/*这样当再一次测试时间时新的tickcount产生了
它来减去上一次的tickcount就得出了一个时间间隔,这个就可以和intervel比较从而得出是否
激活了
*/
return0;
}实现了以上的操作后,接下来要实现界面的设置一方便盒子在界面上的显示和操作;
initMap(void){/*初始化地图*//*
我们须要一圈卫兵呵呵,全是1...用来保证方块不会出来*/
intx,y;
for(y=0;yfor(x=0;xif(x<2||x>MAX_X-3||y>MAX_Y-3)
map[y][x]=1;
elsemap[y][x]=0;
}
}/*这里初始化出这个形状*/
}/*当然是无盖的...*/
voidrender(void)/*这里唯一的绘图函数*/
intx,y;
staticintcPage=0;/*当前页,换页用*/
#defineSTARTX50/*定义几个常量*/
#defineSTARTY0/*数值根据自己的需要可以自己设置*/
#defineLEN18
setactivepage(cPage=(cPage==01:
0));/*选择页*/
cleardevice();/*清屏*/
setcolor(12);/*前景颜色*/
prscore();setcolor(15);
rectangle(STARTX+LEN*2-2,
STARTY+LEN*3-2,
STARTX+LEN*(MAX_X-2)+2,
STARTY+LEN*(MAX_Y-2)+2);
/*用白色画一个外框*/
setfillstyle(SOLID_FILL,5);
for(y=3;yfor(x=2;xif(map[y][x]){
rectangle(x*LEN+STARTX,
y*LEN+STARTY,
x*LEN+STARTX+LEN,
y*LEN+STARTY+LEN);
bar(x*LEN+STARTX+1,
y*LEN+STARTY+1,
x*LEN+STARTX+LEN-2,
y*LEN+STARTY+LEN-2);
}
}
}
/*绘图操作就不要作太复杂的介绍了,这只写作用*/
/*以上段,根据地图上的点阵情况将地图反映到屏幕上*/
for(y=0;y<5;y++){/*画下落物*/
for(x=0;x<5;x++){
if(curbox[y][x]){
if(y+cury>2){
rectangle((x+curx)*LEN+STARTX,
(y+cury)*LEN+STARTY,
(x+curx)*LEN+STARTX+LEN,
(y+cury)*LEN+STARTY+LEN);
bar((x+curx)*LEN+STARTX+1,
(y+cury)*LEN+STARTY+1,
(x+curx)*LEN+STARTX+LEN-2,
(y+cury)*LEN+STARTY+LEN-2);
}
}
}
}
/*以上将下落的盒子按昭它在地图上的坐标,画到对应的区域里*/
for(y=0;y<5;y++){/*画下一个*/
for(x=0;x<5;x++){
if(nextbox[y][x]){
rectangle(x*LEN+320,
y*LEN+10,
x*LEN+338,
y*LEN+28);
bar(x*LEN+321,
y*LEN+11,
x*LEN+336,
y*LEN+26);
}
}
}
/*这个画出下一个盒子的预览*/
setvisualpage(cPage);/*确认在cPage页里画好了*/
/*将它显示出来*/
}
这样我们的初始化界面和盒子在图上的意义显示已经完成;
5.接下里我们要对即将出现的方块的形状进行预览即建立一个新的函数用来显示下一个下落的盒子;
voidrebuidNext(){/*新建下一个形状并放到nextbox中*/
inti,x,y;
i=random(MAX_C);/*从几种方块里面选一种*/
for(y=0;y<5;y++)/*并复制过来*/
for(x=0;x<5;x++)
nextbox[y][x]=box[i][y][x];/*复制*/
}
voidputBox(){/*将curbox填充到地图上*/
intx,y;
for(y=0;y<5;y++)/*这个也简单,主要是要根*/
for(x=0;x<5;x++)/*据curx,cury指出位置*/
if(curbox[y][x])
map[y+cury][x+curx]=curbox[y][x];
}
intnewfall(){/*创建下落元素失败返回0*/
intx,y;
curx=MAX_X/2-2;/*重新指定小盒位置*/
cury=0;
for(y=0;y<5;y++)
for(x=0;x<5;x++)
curbox[y][x]=nextbox[y][x];/*将nextBox复制过来*/
rebuidNext();/*重建nextBox*/
returntest(curx,cury,curbox);
}
这样一来,我们的预览盒子也就完成了。
4.完成以后,我们要进行下落的控制,这个在开始的控制指针已经做好的下落间隔设置,然后开始随机出示方块,玩家应开始进行旋转,移动的操作,下落的过程;
voidrotateBox(intbox1[5][5],intbox2[5][5]){
/*旋转box1输出到box2*/
intx,y;
for(x=0;x<5;x++)/*这个函数可以须要实际*/
for(y=4;y>=0;y--)/*编写一下才能印像深刻*/
box2[y][x]=box1[x][4-y];
}
introtate()/*整个旋转的操作并将盒子打印到屏幕上*/{
intx,y;
intnewbox[5][5];/*我们必须将当前盒子转动到新的盒子*/
/*再对这个新的盒子的冲突作测试*/
rotateBox(curbox,newbox);/*转动到新的盒子*/
if(test(curx,cury,newbox)){
/*并且新的盒子能放到地图上而不冲突*/
for(y=0;y<5;y++)
for(x=0;x<5;x++)
curbox[y][x]=newbox[y][x];/*复制进来*/
return1;
}
elsereturn0;
}
intmove(intdir){/*返回成功与否*/
intnewx;
if(dir)newx=curx+1;
/*与drop一样,准备移动后的坐标*/
elsenewx=curx-1;
if(test(newx,cury,curbox)){/*测试是否冲突*/
curx=newx;/*可以的话切换curx*/
return1;
}
return0;
}这个就完成了左右移动的操作;
intdrop(){/*下落,返回成功与否*/
intnewy;/*盒子要下落的新位置*/
newy=cury+1;/*为当前Y位置+1*/
if(test(curx,newy,curbox)){
cury=newy;/*测试下落盒在这个位置*/
return1;/*上是否有冲突,没有的话*/
}/*直接设置cury*/
return0;
}
inttest(intmx,intmy,intbox[5][5]){
/*测试box在map里mx,my位置上是否能着陆*/
/*这个是最关键的一个函数,它判断是否产生非空冲突*/
/*但算法还是很简单的*/
intx,y;
for(y=0;y<5;y++)
for(x=0;x<5;x++)
if(map[y+my][x+mx]&&box[y][x])
return0;
return1;
}
这样就完成了旋转,移动,测试并下落的功能。
6.接下来我们要完成下落后的分数的改变速度的重新设定;
voidclear(){/*清除掉满行*//*具体的算法为:
从第0行开始到最后一行,测试地图点阵是否为满,如果是的话
从当前行算起,之上的地图向下掉一行*/
intx,y;
intdx,dy;
intfullflag;
for(y=0;yfullflag=1;/*假设为满*/
for(x=2;xif(!
map[y][x]){
fullflag=0;
break;
}
}
if(fullflag){/*向下移动一行*/
for(dy=y;dy>0;dy--)
for(dx=2;dxmap[dy][dx]=map[dy-1][dx];
for(dx=2;dxmap[0][dx]=0;score+=10;/*输出新得分*/
/*并清除掉第一行*/
}
}
}
voidprscore()/*输出分数*/{
charstr[10];setfillstyle(SOLID_FILL,YELLOW);
rectangle(90,15,260,35);setcolor(6);
settextstyle(0,0,2);sprintf(str,"score:
%d",score);
outtextxy(115,20,str);}
voidspe()/*显示速度的改变*/{
if(score%50==0&&score!
=0)setTimer(&tDown,speed--,1);}
7.然后在主函数之前中定义以下的操作,这个应该会很容易理解
intmap[MAX_Y+4][MAX_X+4];/*地图\大盒子...MAX_X,Y是可见面积*/
/*我已说过需要在外面布两圈"卫兵"*/
intcurbox[5][5];/*当前下落的盒子*/
intcurx,cury;/*保存着当前活动盒子在地图上的位置*/
intnextbox[5][5];/*保存着下一个形状的盒子*/
8.接下俩用newgame函数将其串联起来,实现函数的内嵌;
voidnewGame(){/*新建游戏*/
intx,y;
initMap();/*初始化地图*/
srand(GetTickCount());/*初始化随机发生器*/
rebuidNext();/*建立下一个*/
setTimer(&tDown,speed,1);/*启动时钟(快慢两个)*/
setTimer(&tFast,FAST_INTV,1);
newfall();/*对下落的盒子操作一下*/
/*这样第一个下落的方块
就在地图顶部准备好了*/
}
9.接下来最为重要的主函数将要开始;
intmain(){
charkey;/*记录当前按键*/
inti;
intgd=VGA,gm=VGAMED;/*初始化的图形模式*/
Timer*ptDown;/*下落所指向的时钟(有快慢)*/
Timertrender;/*为了避免渲染给程序造成过大的负担*/
/*用一个时钟来控制渲染速度*/
/*把它设置interval=1,*/
/*这样就是18FPS了,当然无法达到标*/
/*准的60FPS...毕竟这是DOS...*/
setTimer(&trender,1,1);
initgraph(&gd,&gm,"");/*初始化图形*/
newGame();/*新游戏...*/
prscore();
while
(1){/*主游戏循环*/
if(kbhit()){/*如果键盘有按下*/
key=getch();/*读取一个按键值到key*/
}
elsekey=0;
switch(key){/*对读到的key进行判断*/
caseKEY_UP:
rotate();/*上,旋转下落盒子*/
break;
caseKEY_DOWN:
ptDown=&tFast;/*使用tFast时钟*/
break;
caseKEY_LEFT:
move(0);/*左移*/
break;
caseKEY_RIGHT:
move
(1);/*右移*/
break;
caseKEY_ESC:
closegraph();/*结束游戏*/
exit(0);
default:
ptDown=&tDown;/*使用原来速度*/
}
if(testTimer(ptDown)){/*在上面已设置了下落要
使用的时钟在ptDown里*/
if(!
drop()){/*下落,失败返回0*/
putBox();/*写到地图里*/
clear();prscore();spe();setcolor(6);/*清除满行*/
if(!
newfall()){/*新建下落,失败则游戏结束*/
setcolor(14);
settextstyle(0,0,2);
outtextxy(100,200,"GAMEOVER");/*游戏结束,在屏幕上打印字符串*/
outtextxy(100,240,"THANKYOU");
sleep(3);
exit(0);
}
}
}
if(testTimer(&trender))/*最后...渲染...*/
render();
}
}
10.最后main函数已经介绍完毕,只需在开始加上程序所需的头文件和值的宏定义后整个程序就全部完毕
#include<>
#include<>
#include<>/*这里须要读取系统运行时间来作为定时器*/
#include<>/*很