程序设计实践
设计报告
课题名称: 彩色控制台下的俄罗斯方块
学生姓名:
班 级:________ ___________
班内序号:
学 号:
日 期: 20##年4月29日
1. 课题概述
1.1课题目标和主要内容
本程序采用vs2005在控制台下编写了彩色俄罗斯方块游戏,能够实现消行、反转、计分、改变下落速度、分难度等级游戏等功能。
1.2系统的主要功能
1.可以灵活控制方块在图形框中运动。
2.游戏过程中方块可以自由旋转。
3.当某一行的方块排列满时,将自动将这一行方块消除,然后将上面所有方块向下移动,可以支持连续消行。并且当连续消行时分数会有一定奖励。
4.可以提示下一个出现的方块
5.游戏前可以选游戏的等级,不同等级对应不同速度,不同等级消行后的加分不同,等级越高分数越高 ,方便不同水平的玩家游戏。
6.游戏结束后弹出对话框提醒结束游戏还是继续游戏。
7.初始化界面和游戏中有音乐,使游戏更吸引人。
2. 系统设计
2.1 系统总体框架
2.2 系统详细设计
[1] 模块划分图及描述
[2] 类关系图及描述
[3] 程序流程图及描述
[4] 存储结构、内存分配
上述4个内容也可以按照模块划分分别设计。
2.3 关键算法分析
算法1:getblocks()
[1] 算法功能
生成随机方块
[2] 算法基本思想
利用srand() rand()函数产生随机数,随机数对7取余得到0~6的随机数,对应7种随机方块。
[3] 算法空间、时间复杂度分析
空间复杂度O()
时间复杂度O(1)
[4] 代码逻辑(可用伪代码描述)
getblocks() //随机方块生成
int * getblocks() //随机方块生成
{
int * m=NULL;
srand(time(NULL));
int n=rand()%7;
switch(n)
{
case 0:
m=&a1[0][0];break;
case 1:
m=&a2[0][0];break;
case 2:
m=&a3[0][0];break;
case 3:
m=&a4[0][0];break;
case 4:
m=&a5[0][0];break;
case 5:
m=&a6[0][0];break;
case 6:
m=&a7[0][0];break;
}
return m;
}
算法2:move
[1] 算法功能
方块的移动、下落等
[2] 算法基本思想
通过_kbhit()和_getch()函数获得键盘输入,通过键盘控制方块的加速下落、翻转及左右移动
[3] 算法空间、时间复杂度分析
空间复杂度O(1)
时间复杂度O(1)
[4] 代码逻辑(可用伪代码描述)
void move(int line) //方块的左右移动,加速下落,翻转等
{
int mid=0,speed=100-10*level;
while(mid<speed)
{
if (_kbhit())
{
switch(_getch())
{
case 72: //翻转
{
turn(line);
break;
}
case 75: //左移
{
row=row-2; //纵坐标减
if(isavailable(line)) //判断是否能移动
{
row=row+2;
clearsquare(line+1); //消除原来图案,line+1是避免line=4程序出错
row=row-2;
drawblocks(line); //出现新图案
}
else
row=row+2; //若不能移动则纵坐标不变
break;
}
case 77: //右移
{
row=row+2;
if(isavailable(line))
{
row=row-2;
clearsquare(line+1);
row=row+2;
drawblocks(line);
}
else
row=row-2;
break;
}
case 80: //加速下落,即直接跳除循环
{
mid=speed;
break;
}
case 27: //终止游戏
{
end();
break;
}
case 32: //暂停
{
int flag=1;
while(flag)
{
if (_kbhit())
{
if(_getch()==32)
flag=0;
break;
}
else
Sleep(10);
}
}
default:
break;
}
}
Sleep(8); //使方块延迟
mid++;
}
}
3. 程序运行结果分析
标题“欢迎来到俄罗斯方块”采用了闪烁文字;
下个方块形状随机;
消行时动画提示;
4. 总结
4.1课题的难点和关键点
比如调试方法、程序优化和改进、消息机制、屏幕刷新、网络传输等方面,用什么方法解决了的什么问题。
程序设计上,封装的函数较多,使主函数main()看起来比较简洁,而封装的函数可以重复运用,也减少了代码的长度。采用了丰富的条件结构和循环结构,代码比较简约。此外,在关键处,均添加了注释,方便了自己和其他人对代码的检查。
4.2 本课题的评价
本次实验所要求的基本功能书上大部分都有代码和解析,所以完成起来并不是十分困难。只是游戏的各个功能与环节的安排与相互调用对于新手来说很困难,调试起来出现了很多bug,需要一一解决。运行效果上,整体来看,所设计功能全部得到了实现,效果比较理想。但是也存在不足之处,如方块在边缘的位置时,无法进行旋转。另外,原本设计的双人游戏由于时间原因,未能实现。
4.3心得体会
我这次实验做的是俄罗斯方块,开始时以为没什么难度,可是当真正着手做时,发现很多东西并不容易实现。比如:如何显示每一个俄罗斯小方块,如何预测下一个俄罗斯小方块,如何控制每一个方块的下落速度,如何判断俄罗斯方块是否可以停止,如何通过键盘控制俄罗斯方块的位置和方向等。如果有一行或多行满行,如何删除这些行,并重新绘制游戏区界面,同时分数如何计算并显示等等,都要一一实现。真是难上加难。 不过,当我仔细研究了书上给出的算法后,这些问题被一一破解。
当然,在这次课程设计中,也出现了许多错误或麻烦。主要有一些判断条件的制定。有些判断条件没有想全,当执行某些操作时就出现异常,比如:俄罗斯方块的停止条件的判断,如果判断条件不完整就会出现下落的方块遇到已经存在的方块后还继续下落。
通过这次课程设计,我对游戏编程有了初步认识,对游戏算法有了一定了解。从游戏的需求分析,系统设计,再到游戏代码实现每一步都对自己是一个挑战,虽然过程充满艰辛,但是从中我学到了很多课本上学不到的编程经验,也从中获得了很多快乐。
5. 参考文献
[1]《c++高级语言程序设计案例与实践辅导》 徐惠民 人民邮电出版社 20##年
[2]《c++高级语言程序设计》 徐惠民 人民邮电出版社 20##年
附:头文件
#include <windows.h>
#include <iostream>
using namespace std;
HANDLE initiate();
BOOL textout(HANDLE hOutput,int x,int y,WORD wColors[],int nColors,LPTSTR lpszString);
源程序1
#include "colorConsole.h"
HANDLE initiate()
{
HANDLE hOutput;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
return hOutput;
}
BOOL textout(HANDLE hOutput,int x,int y,WORD wColors[],int nColors,LPTSTR lpszString)
{
DWORD cWritten;
BOOL fSuccess;
COORD coord;
coord.X = x; // start at first cell
coord.Y = y; // of first row
fSuccess = WriteConsoleOutputCharacter(
hOutput, // screen buffer handle
lpszString, // pointer to source string
lstrlen(lpszString), // length of string
coord, // first cell to write to
&cWritten); // actual number written
if (! fSuccess)
cout<<"error:WriteConsoleOutputCharacter"<<endl;
for (;fSuccess && coord.X < lstrlen(lpszString)+x; coord.X += nColors)
{
fSuccess = WriteConsoleOutputAttribute(
hOutput, // screen buffer handle
wColors, // pointer to source string
nColors, // length of string
coord, // first cell to write to
&cWritten); // actual number written
}
if (! fSuccess)
cout<<"error:WriteConsoleOutputAttribute"<<endl;
return 0;
}
源程序2
#include <conio.h>
#include <iostream>
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "WINMM.LIB")
#include <stdlib.h>
#include <ctime>
#include "colorConsole.h"
using namespace std;
void begin(); //开始游戏
void frame(); //边框设定
int * getblocks(); //方块产生
void move(int line); //移动
void drawblocks(int line); //方块显示
void clearsquare(int line); //方块擦除
void turn(int line); //方块旋转
bool isavailable(int line); //判断是否能下落
void remember(int line); //记忆方块位置
void deleteline(int line); //方块满一行消除
bool ifgameover(); //判断是否游戏结束
void end(); //游戏结束
#define up 72
#define down 80
#define left 75
#define right 77
#define esc 27
HANDLE handle;
int a1[4][4]={{1},{1,1,1}}; //七种方块的二维数组
int a2[4][4]={{0,1},{1,1,1}};
int a3[4][4]={{1,1},{0,1,1}};
int a4[4][4]={{0,0,1},{1,1,1}};
int a5[4][4]={{0,1,1},{1,1}};
int a6[4][4]={{1,1,1,1}};
int a7[4][4]={{1,1},{1,1}};
int row=0; //列数
int score=0; //分数
int level=0;
int * block1=NULL;
int * block2=NULL;
int * block3=NULL;
int coordinate[12][18]={0}; //坐标数组,边框*18(最后一行,两边边框计算在内)
int judge=0;
int scorex=0;
int temp[4][4]={0};
void main() //主函数
{
int t=1;
handle = initiate();
while(t)
{
t=0;
begin();
sndPlaySound("H:/music.wav",SND_LOOP|SND_ASYNC);
frame();
WORD wColors[1];
wColors[0]=FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY;//黄色
for(int k=1;k<=999999;k++)
{
if(ifgameover()) //判断是否结束
{
textout(handle,34,10,wColors,1,"Game Over");
Sleep(80);
end();
}
else
{
if(k==1)
block2=getblocks();
block3=block2; //block2指向将出现的方块地址
block2=getblocks(); //获取下一个新的方块
block1=block3;
row=52;
clearsquare(16); //擦除next的方块
block1=block2;
drawblocks(15); //在next显示下一块方块图形
row=34;
block1=block3;
for(int i=4;i<=7;i++) //所构建的方块图形最多只有占有两排,所以只用-7即可对应
{
if(*(block1+i))
textout(handle,26+i*2,4,wColors,1,"■"); //方块先露出下面部分
}
Sleep(500-50*level);
for(int line=4;line<=22;line++) //方块自主下落,方块从第四排开始出现
{
if(isavailable(line)) //检验刚产生的方块是否碰壁,碰到已落方块
{
clearsquare(line); //消除方块先露初的下面分
drawblocks(line); //产生完整的下落方块
move(line);
}
else
{
remember(line); //落定后将这些位置对应的all数组中元素置
deleteline(line); //消行以及加分
if(line==4)
judge=1;
break;
}
}
}
}
}
}
void begin()
{
int i=1;
WORD wColors[2];
wColors[0]=FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_INTENSITY;//紫色
wColors[1]=FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY;//白色
WORD wColors1[1];
wColors1[0]=FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY;//白色
WORD wColors2[2];
wColors2[0]=FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY;//黄色
wColors2[1]=FOREGROUND_RED|FOREGROUND_INTENSITY;//红色
textout(handle,18,6,wColors,2,"┏━━━━━━━━━━━━┓");
textout(handle,18,7,wColors,2,"┃ ┃");
textout(handle,18,8,wColors,2,"┃ ┃");
textout(handle,18,9,wColors,2,"┃ ┃");
textout(handle,18,10,wColors,2,"┗━━━━━━━━━━━━┛");
textout(handle,26,11,wColors,1," 请选择难度");
textout(handle,26,12,wColors,1," 简单请按");
textout(handle,26,13,wColors,1," 中等请按");
textout(handle,26,14,wColors,1," 困难请按");
textout(handle,23,15,wColors1,1,"MADE BY 通信六班王宏洁 ");
textout(handle,30,16,wColors1,1," 2012210176 ");
while(i)
{
textout(handle,23,8,wColors2,2,"欢迎来到俄罗斯方块");
Sleep(800);
textout(handle,23,8,wColors2,2," ");
Sleep(800);
if (_kbhit()) //输入等级
{
switch(_getch())
{
case '1':
{
level=1;
i=0; //跳出循环
break;
}
case '2':
{
level=4;
i=0;
break;
}
case '3':
{
level=7;
i=0;
break;
}
}
}
}
system("cls"); //清屏
}
void frame() //边框的设定
{
WORD wColors[1];
wColors[0]=FOREGROUND_BLUE|FOREGROUND_INTENSITY; //蓝色
WORD wColors1[1];
wColors1[0]=FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY; //蓝色
for(int i=0;i<=11;i++)
coordinate[i][17]=1; //底排边框定义为
for(int j=0;j<=17;j++)
{
coordinate[0][j]=1; //两边边框定义为
coordinate[11][j]=1;
}
char string[5];
textout(handle,59,5,wColors,1,itoa(level,string,10));
textout(handle,52,5,wColors,1,"level: ");
textout(handle,52,9,wColors,1,"score: 0");
textout(handle,52,13,wColors,1,"next:");
textout(handle,10,6,wColors1,1,"暂停 SPACE");
textout(handle,10,7,wColors1,1,"退出 ESC");
textout(handle,10,8,wColors1,1,"翻转 ↑");
textout(handle,10,9,wColors1,1,"向右 →");
textout(handle,10,10,wColors1,1,"向左 ←");
textout(handle,10,11,wColors1,1,"加速 ↓");
textout(handle,33,2,wColors,1,"加油!");
for(int m=13;m<=24;m++)
{
textout(handle,2*m,3,wColors,1,"┅"); //上边框
}
for(int n=4;n<=21;n++)
{
textout(handle,26,n,wColors,1,"┇"); //左边框
}
for(int k=4;k<=21;k++)
{
textout(handle,48,k,wColors,1,"┇"); //右边框
}
for(int l=13;l<=23;l++)
{
textout(handle,2*l,21,wColors,1,"┅"); //下边框
}
textout(handle,26,3,wColors,1,"◤");
textout(handle,48,3,wColors,1,"◥");
textout(handle,26,21,wColors,1,"◣");
textout(handle,48,21,wColors,1,"◢");
}
int * getblocks() //随机方块生成
{
int * m=NULL;
srand(time(NULL)); //随机数发生器的初始化函数
int n=rand()%7; //生成随机数
switch(n)
{
case 0:
m=&a1[0][0];break;
case 1:
m=&a2[0][0];break;
case 2:
m=&a3[0][0];break;
case 3:
m=&a4[0][0];break;
case 4:
m=&a5[0][0];break;
case 5:
m=&a6[0][0];break;
case 6:
m=&a7[0][0];break;
}
return m;
}
void drawblocks(int line) //出现方块
{
WORD wColors[1];
wColors[0]=FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY;//黄色
for(int j=0;j<=15;j++)
{
int temp;
temp=j/4;
if(*(block1+j))
textout(handle,row+j*2-temp*8,line+temp,wColors,1,"■");
}
}
void clearsquare(int line) //方块消失
{
WORD wColors[1];
wColors[0]=FOREGROUND_BLUE|FOREGROUND_INTENSITY;
if(line==4) //针对消除刚产生的下排
{
textout(handle,34,4,wColors,1," ");
textout(handle,36,4,wColors,1," ");
textout(handle,38,4,wColors,1," ");
textout(handle,40,4,wColors,1," ");
}
else
{
for(int m=0;m<=15;m++)
{
int temp;
temp=m/4; //得-3对应方块数组-4行
if(*(block1+m))
textout(handle,row+m*2-temp*8,line-1+temp,wColors,1," ");
}
}
}
void move(int line) //方块的左右移动,加速下落,翻转等
{
int mid=0,speed=100-10*level;
while(mid<speed)
{
if (_kbhit())
{
switch(_getch())
{
case 72: //翻转
{
turn(line);
break;
}
case 75: //左移
{
row=row-2; //纵坐标减
if(isavailable(line)) //判断是否能移动
{
row=row+2;
clearsquare(line+1); //消除原来图案,line+1是避免line=4程序出错
row=row-2;
drawblocks(line); //出现新图案
}
else
row=row+2; //若不能移动则纵坐标不变
break;
}
case 77: //右移
{
row=row+2;
if(isavailable(line))
{
row=row-2;
clearsquare(line+1);
row=row+2;
drawblocks(line);
}
else
row=row-2;
break;
}
case 80: //加速下落,即直接跳除循环
{
mid=speed;
break;
}
case 27: //终止游戏
{
end();
break;
}
case 32: //暂停
{
int flag=1;
while(flag)
{
if (_kbhit())
{
if(_getch()==32)
flag=0;
break;
}
else
Sleep(10);
}
}
default:
break;
}
}
Sleep(8); //使方块延迟
mid++;
}
}
void turn(int line)
{
clearsquare(line+1); //消除原来的图案
int b[4][4]={0}; //保存旋转前的方块
int num=0,l=0;
for(int m=0;m<=3;m++)
{
for(int n=0;n<=3;n++)
{
b[m][n]=*(block1+m*4+n); //把b[4][4]全赋值为当前图形数组
temp[m][n]=0;
}
}
for(int i=3;i>=0;i--) //按行从下向上扫描
{
for(int j=0;j<4;j++) //按列从左向右扫描
{
if(b[i][j]) //如果为有效点,则进行度旋转
{
temp[j][l]=b[i][j];
num=1;
}
}
if(num)
{
l++;
num=0;
}
}
block1=&temp[0][0];
if(isavailable(line))
drawblocks(line);
else
{
for(int p=0;p<=3;p++)
{
for(int q=0;q<=3;q++)
temp[p][q]=b[p][q];
}
block1=&temp[0][0];
drawblocks(line);
}
}
bool isavailable(int line) //检验,即看方块即将存在位置是否已经有
{
int x,y;
for(int m=0;m<=15;m++)
{
int temp;
temp=m/4;
x=row/2-13-4*temp+m; //边框左边已有个位置
y=line-4+temp; //上面已有个位置
if(*(block1+m)&&coordinate[x][y]) //相与为则返回,否则跳出并循环继续
return 0;
}
}
void remember(int line) //记忆
{
int x,y;
for(int m=0;m<=15;m++)
{
int temp;
temp=m/4;
x=row/2-13-temp*4+m;
y=line-4+temp;
if(*(block1+m)) //如果当前位置为,则返回原位置,并设置为
coordinate[x][y-1]=1;
}
}
void deleteline(int l) //消行
{
WORD wColors[1];
wColors[0]=FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY;//黄色
int snum=0,b=0;
for(int m=0;m<=16;m++) //从上向下消去方块
{
if(coordinate[1][m]==1&&coordinate[2][m]==1&&coordinate[3][m]==1&&coordinate[4][m]==1&&coordinate[5][m]==1&&coordinate[6][m]==1&&coordinate[7][m]==1&&coordinate[8][m]==1&&coordinate[9][m]==1&&coordinate[10][m]==1)
{
textout(handle,28,m+4,wColors,1,"﹌﹌﹌﹌good﹌﹌﹌﹌");
Sleep(750);
for(int n=1;n<=m;n++)
{
for(int j=1;j<=10;j++)
coordinate[j][m-n+1]=coordinate[j][m-n];
}
snum++;
}
}
for(int n=1;n<=10;n++)
{
for(int d=0;d<=16;d++)
{
int x,y;
x=n*2+26;
y=d+4;
textout(handle,x,y,wColors,1," ");
if(coordinate[n][d])
{
textout(handle,x,y,wColors,1,"■");
}
}
}
score=score+(snum*(snum+1)/2);
if((score-scorex)>=30) //每得到分自动加速
{
level++;
scorex=score;
}
char string[5];
textout(handle,59,9,wColors,1,itoa(score,string,10));
textout(handle,59,5,wColors,1,itoa(level,string,10));
}
bool ifgameover() //终止游戏
{
if(judge==1)
{
return 1;
}
else
return 0;
}
void end() //退出
{
WORD wColors[1];
wColors[0]=FOREGROUND_GREEN|FOREGROUND_INTENSITY; //绿色
textout(handle,28,22,wColors,1,"Press any key to exit");
while(1)
{
if (_kbhit())
{
exit(EXIT_SUCCESS);
}
}
}