载入中
自定义HTML载入中... loading
俄罗斯方块编程分析 [转贴 2008-04-03 14:56:34]  删除... 
字体变小 字体变大

俄罗斯方块编程分析

 

大家都比较对游戏感兴趣吧!下面对比较熟悉QQ游戏中的------“火拼俄罗斯”进行分析俄罗斯方块游戏的程序中用到了一些方法。为了比较容易理解这些方法,我在讲述的同时写了些专门针对这些方法的示例程序。这些示例程序力求短小,目的是用最小的代码能够清楚的示例所用的方法。本人初学编程,文章有不足之处还请指教[很有骗稿嫌疑]。这些示例程序都是经过tc2.0测试。附了完整的俄罗斯方块游戏的源代码,提供调试 。接下来。我分九个步骤来分析吧。
(一)首先,这个游戏有如下几个问题要解决: 我把流程图写到主函数旁边
main(int argc,char *argv[])
{
if (argc!=1)
{
if (argv[1]!="") Heng=atoi(argv[1]);
if (argv[2]!="") Shu=atoi(argv[2]);
}
Init();               /*初始化界面*/
PreAct=random(8);         /*取得当前的方块*/
for(;;)                 /*以下是游戏流程*/
{
NextAct=random(8);       /*取得下一个方块*/
DrawNext(1);           /*画出下一个方块*/
Act=PreAct;             
if (Heng%2==0) ActH=Heng/2; else ActH=(Heng-1)/2; 
ActS=0;             /*方块开始从游戏空间的中间下落*/
Staus=0;           /*取开始的状态*/
NoPass=CAN;         /*物体可以下落*/
Give();             /*取得当前的方块*/
Display(Act+1);       /*显示当前的方块,每种方块的颜色不同*/
GoOn();             /*游戏的算法精髓所在*/
PreAct=NextAct;       /*方块下落完毕,取得下一个方块*/
DrawNext(0);   
}
}
c2.0中怎么样设置图形显示?Tc2.0中有两种显示模式,一种是我们所熟知的字符模式,另一种是图形模式。在字符模式下只能显式字符,如ASCII字符。一般是显示25行,每行80个字符。程序缺省的是字符模式。在字符模式下不能显式图形和进行绘图操作。要想进行图形显示和绘图操作,必须切换到图形模式下。
Tc2.0中用initgraph()函数可以切换到图形模式,用closegraph()可以从图形模式切换回字符模式。initgraph()和closegraph()都是图形函数,使用图形函数必须包括头文件"graphics.h"。void far initgraph(int far *graphdriver,int far *graphmode,char far *pathtodriver);graphdriver是上涨指向图形驱动序号变量的指针;graphmode是在graphdriver选定后指向图形显示模式序号变量的指针。pathtodriver表示存放图形驱动文件的路径。
Tc2.0中有多种图形驱动,每种图形驱动下又有几种图形显示模式。在我的程序中图形驱动序号为VGA,图形显示模式序号为VGAHI。这是一种分辨率为640*480(从左到右坐标依次为0-639,从上到下坐标依次为0-479),能够显示16种颜色的图形模式。别的图形驱动序号和图形显示模式序号,可以从手册或联机帮助中找到。
pathtodriver指示存放图形驱动文件的路径。图形驱动序号不同,图形驱动文件也不同。序号为VGA图形驱动对应"egavga.bgi"这个图形驱动文件。"egavga.bgi"一般在Tc目录下。void far closegraph(void);没有参数,从图形模式直接返回字符模式。
initgraph()和closegraph()的常用用法如下:
int gdriver = VGA, gmode=VGAHI, errorcode;
/* initialize graphics mode */
initgraph(&gdriver, &gmode, "e:\\tc2");
/* read result of initialization */
errorcode = graphresult();
if (errorcode != grOk) /* an error occurred */
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1); /* return with error code */
}
/* return to text mode */
closegraph();
Tc2.0中常用图形函数的用法?
在这里讲几个游戏中用到的绘图用的图形函数:
setcolor();
rectangle();
outtextxy();
setfillstyle();
bar();
void far setcolor(int color);
  设置画线、画框和在图形模式下显示文字的当前颜色。这个函数将影响line()、rectangle()和outtext xy()函数绘图的颜色。
color可以取常的颜色常量:
BLACK ? 0
BLUE ? 1
GREEN ? 2
CYAN ? 3
RED ? 4
MAGENTA ? 5
BROWN ? 6
LIGHTGRAY ? 7
DARKGRAY ? 8
LIGHTBLUE ? 9
LIGHTGREEN ?10
LIGHTCYAN ?11
LIGHTRED ?12
LIGHTMAGENTA ?13
YELLOW ?14
WHITE ?15
void far rectangle(int left,int top,int right,int bottom);
用当前颜色画一个左上角为(left,top)、右下角为(right,bottom)的矩形框。
void far outtextxy(int x,int y,char * textstring);
在(x,y)处用当前字体(缺省的字体是DEFAULT_FONT)显示字符串textstring,字符串的对齐方式由settext justify()指定。
void far setfillstyle(int pattern,int color);
设置图形的填充模式和填充颜色,主要影响bar()等函数。
pattern一般取枚举常量值SOLID_FILL,color的取值与setcolor(int color)中color的取值范围相同。
(二) 速度控制,这个速度控制应不受用户按键的影响.每隔一定时间下落的方块(以下简称下落物)就无条件下落,并且随着时间的延长速度越来越快.因此可以通过一个系统变量来控制.其值越来越小.下面程序用变量float Delays=15000;Delays控制.闲时可用delay(Delays)进行延时.
(三)下落物的表示.也就是表示方块的数据结构(4X4的数组)可以有各种形式下落物,并且每个下落物又有几种状态.为了表示他们,可以用数组表示,鉴于其形状特点,建一个4X4的数组,数组值为0则表示该位不显示,为1则显示。这里有个不好处理的地方是每种下落物的状态个数不同。有的只有一个状态,有的却有四个状态。为了处理方便,全部定义为四个状态,只是部分有重复状态而已。
定义如下:下列原代码用Act来定义方快的种数!
int a[8][4][4][4]={{{1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0},
/*■■■■是显示这个图形*/
  {1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
  {1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0},
  {1,1,1,1,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}, 
/*         ■■
            ■■显示这个图形的数组
                                      */
  {1,1,0,0,1,1,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},
  {1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0}},
{{1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0}, 
/*       ■   
        ■■■是显示这个图形/*
  {0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0},
  {0,1,0,0,1,1,1,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}},
{{1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0},
/* ■
    ■■
      ■*/
  {0,1,1,0,1,1,0,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,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0}},
{{0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0},
/*   ■
    ■■
      ■     */
  {1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0},
  {0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0},
  {1,1,0,0,0,1,1,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},
/*   ■
    ■■■*/
  {1,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0},
  {1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0},
  {0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0}},
{{0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0},   
    ■
  /*■■■*/
  {1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0},
  {1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0},
  {1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0}},
{{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/   *■
    ■*/
  {1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  {1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  {1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}};
你可以自己添加你想要的方块!!!
(四)   
游戏空间指的是整个游戏主要的界面(呵呵,这个定义我实在想不出更准确的,还请哪位大虾指点)。实际上是 
一个宽12格子、高20格子的
游戏板。用一个全局数组position[30][30]表示。 
for (i=0;i<MAX;i++)
for (j=0;j<MAX;j++)
{Position[j]=1;
middle[j]=-1;
}
for (i=0;i<Heng;i++)
for (j=0;j<Shu;j++)
Position[j]=0;
for (i=0;i<Heng;i++)
for (j=0;j<Shu;j++)
DrawBox(i,j,0);表示的时候:position[x][y]为1时表示游戏板上(x,y)这个位置上已经有方块占着了,
position[x][y]为0表示游戏板上这位置还空着。为了便于判断形状的移动是否到边、到底,初始的时候
把数组初使化大一些!然后定义一个position[12][20]游戏空间!
表示方法同下落物,为0表示该位没有方块,为1有方块,显示出来。
(五) 
下落处理。在下落过程中,有两种可能,一种是半空被挂住,一种是
落到底部。无论哪种都不能再继续。
我的处理办法是,把下落物的数组和下落区的数组对应相加。由于二
者值都或为0或为1,因此如果出现2,则表示两个有重合的,这样当然
就不能下降了。恢复办法也很简单,就是相减一下就变成原来的了。
当然这里还有一个必须考虑的地方就是落到底了,也得停,不然就落
到无底洞了。
int Touch(int x,int y,int dx,int dy)
{
NoPass=CAN;
for (i=0;i<4;i++)
for (j=0;j<4;j++)
Position[x+dx+i][y+dy+j]+=b[j];
for (i=0;i<MAX;i++)
for (j=0;j<MAX;j++)
if (Position[j]>1) NoPass=CANNOT;
for (i=0;i<4;i++)
for (j=0;j<4;j++)
Position[x+dx+i][y+dy+j]-=b[j];
if (NoPass==CANNOT && dx==0 && dy==1)
{
for (i=0;i<4;i++)
for (j=0;j<4;j++)
Position[x+i][y+j]+=b[j];
NoPass=BOTTOM;
}
return NoPass;
}
(六) 
每一排充满后要消掉。这个处理相对简单,为了刺激玩者,还可以对同时消多排的加大分量。比如同时消两排的分数要比单独两次消一排的分数要多。
void DetectFill()
{
int Number,Fall,FallTime=0;
for (i=Shu-1;i>=0;i--)
{
Number=0;
for (j=0;j<Heng;j++)
if (Position[j]==1) Number++;
if (Number==Heng)
{
FallTime++;
if (Sounds==CAN)
{
sound(500);
delay(500);
nosound();
}
for (Fall=i;Fall>0;Fall--)
for (j=0;j<Heng;j++)
{
Position[j][Fall]=Position[j][Fall-1];
if (Position[j][Fall]==0) DrawBox(j,Fall,0);
    else DrawBox(j,Fall,1);
}
i++;
}
}
switch(FallTime)
{
case 0:break;
case 1:Scores+=1;break;
case 2:Scores+=3;break;
case 3:Scores+=6;break;
case 4:Scores+=10;break;
}
if (FallTime!=0)
{
GetScores();
if (Scores%100==0) Delays-=1000;
}
}

(七)还有一个,对于TC编程人员来说最困难的可能就是键盘控制了。因为对于DOS系统,可视字符可直接获得ASCII码,但控制字符由于只能获得SCAN扫描码,因此相对比较困难。这里有个函数,用户可方便的一次转换。
int GetKey(void)
{
int Ch,Low,Hig;
Ch=bioskey(0);
Low=Ch&0x00ff;
Hig=(Ch&0xff00)>>8;
return(Low==0?Hig+256:Low);
}
这样,不管用户按哪一个键,都可以直接获得。对于其中ASCII部分,得的结果小于128,对于控制字符(比如上下左右键)得到的大于128。
(八)
怎样控制方块的移动?方块移动的实现很简单,将方块原来的位置用背景色画一个同样大小的方块,将原来的方块涂去。然后在新的位置上重新绘制方块就可以。
void DrawBox(int x,int y,int Color)
{
x=BeginH+x*(Wid+2);
y=BeginS+y*(Wid+2);
setfillstyle(1,Color);
bar(x+2,y+2,x+Wid-1,y+Wid-1);
if (Color==0)
setcolor(9);
else
setcolor(Act+1);
rectangle(x+4,y+4,x+Wid-4,y+Wid-4);
}
这个函数用来画方块!参数x,y用来确定正方形的位置(这是个相对位置哦)
color这个参数很重要!它取两个值0和1;当color为0是用一种颜色填充,这是背景色。将方块原来的位置用背景色画一个同样大小的方块,将原来的方块用背景色填充,这样就实现了方块的移动……这样就实现了方块的移动。这个用方向键控制一个黄色的小方块在屏幕上上、下、左、右移动。这个程序用到了前面几个问题讲的内容,如果你有点忘了,还要回头看看哦。:)
(九) 编译程序。在DOS环境,TC2。0下调试通过。

分类: C/C++
所属版块: 科技
票数:
什么是“我顶”?
点击数:    评论数:
本文章引用通告地址(TrackBack Ping URL)为:
本文章尚未被引用。
发表评论
大 名:
(不填写则显示为匿名者)
网 址:
(您的网址,可以不填)
标 题:
内 容:
请根据下图中的字符输入验证码:
(您的评论将有可能审核后才能发表)
和讯个人门户 v1.0 | 和讯部落 | 客服中心