二.各部分的实现
1.         方块的大小和游戏区的坐标
InBlock.gif const int SquareSize=20;
InBlock.gif const int GameSizeX=300;
InBlock.gif const int GameSizeY=50;
 
方块的边长为20,游戏区从坐标(300,50)处开始。下面是游戏区的长和宽及保存状态的数组。
InBlock.gif const int GameRegionWidth=10;
InBlock.gif const int GameRegionHeight=20;
InBlock.gif struct GameMapStates
InBlock.gif{
InBlock.gif         int states;
InBlock.gif         int shapes;
InBlock.gif}GameMapStatesInfo[10][20];
 
2.         建立游戏区
我们建立一个 20 10 列的网格游戏区,当然这些网格我们是看不见的。在建立网格之 前,我们还需要一些参数。
InBlock.gif int LeftX=GameSizeX; //游戏区左上角横坐标
InBlock.gif int LeftY=GameSizeY; //游戏区左上角纵坐标
InBlock.gif int RightX=LeftX+GameRegionWidth*SquareSize; //游戏区右下角横坐标
InBlock.gif int RightY=LeftY+GameRegionHeight*SquareSize; //游戏区右下角纵坐标
我们利用函数来实现这个功能:
InBlock.gif void DrawGameRegion(HDC hdc)
InBlock.gif{
InBlock.gifHBRUSH brush;
InBlock.gif int LeftX,LeftY,RightX,RightY;
InBlock.gifLeftX=GameSizeX;
InBlock.gifLeftY=GameSizeY;
InBlock.gifRightX=LeftX+GameRegionWidth*SquareSize;
InBlock.gifRightY=LeftY+GameRegionHeight*SquareSize;
InBlock.gifbrush=CreateSolidBrush(RGB(165,180,255));
InBlock.gifSelectObject(hdc,brush);
InBlock.gifRectangle(hdc,LeftX,LeftY,RightX,RightY);
InBlock.gifDeleteObject(brush);
InBlock.gif}
 
3.         初始化俄罗斯方块的 7 种图形
InBlock.gif const POINT Terics[7][4][4] =                    
InBlock.gif{                
InBlock.gif    {                
InBlock.gif        
InBlock.gif    0,0,1,0,0,1,-1,1,                
InBlock.gif        
InBlock.gif    0,0,0,1,1,1,1,2,                
InBlock.gif        
InBlock.gif    0,0,1,0,0,1,-1,1,                
InBlock.gif        
InBlock.gif    0,0,0,1,1,1,1,2                
InBlock.gif        
InBlock.gif    },                
InBlock.gif    {                
InBlock.gif        
InBlock.gif    0,0,1,0,1,1,2,1,                
InBlock.gif        
InBlock.gif    0,0,0,1,-1,1,-1,2,                
InBlock.gif        
InBlock.gif    0,0,1,0,1,1,2,1,                
InBlock.gif        
InBlock.gif    0,0,0,1,-1,1,-1,2                
InBlock.gif        
InBlock.gif    },                
InBlock.gif    {                
InBlock.gif        
InBlock.gif    0,0,0,1,0,2,1,2,                
InBlock.gif        
InBlock.gif    0,0,0,1,-1,1,-2,1,                
InBlock.gif        
InBlock.gif    0,0,1,0,1,1,1,2,                
InBlock.gif        
InBlock.gif    0,0,0,1,1,0,2,0                
InBlock.gif        
InBlock.gif    },                
InBlock.gif    {                
InBlock.gif        
InBlock.gif    0,0,0,1,0,2,-1,2,                
InBlock.gif        
InBlock.gif    0,0,1,0,2,0,2,1,                
InBlock.gif        
InBlock.gif    0,0,1,0,0,1,0,2,                
InBlock.gif        
InBlock.gif    0,0,0,1,1,1,2,1                
InBlock.gif        
InBlock.gif    },                
InBlock.gif    {                
InBlock.gif        
InBlock.gif    0,0,0,1,0,2,0,3,                
InBlock.gif        
InBlock.gif    0,0,1,0,2,0,3,0,                
InBlock.gif        
InBlock.gif    0,0,0,1,0,2,0,3,                
InBlock.gif        
InBlock.gif    0,0,1,0,2,0,3,0                
InBlock.gif        
InBlock.gif    },                
InBlock.gif    {                
InBlock.gif        
InBlock.gif    0,0,1,0,0,1,1,1,                
InBlock.gif        
InBlock.gif    0,0,1,0,0,1,1,1,                
InBlock.gif        
InBlock.gif     0,0,1,0,0,1,1,1,                
InBlock.gif        
InBlock.gif    0,0,1,0,0,1,1,1                
InBlock.gif        
InBlock.gif    },                
InBlock.gif    {                
InBlock.gif        
InBlock.gif    0,0,1,0,2,0,1,1,                
InBlock.gif        
InBlock.gif         0,0,0,1,0,2,1,1,                
InBlock.gif        
InBlock.gif    0,0,0,1,-1,1,1,1,                
InBlock.gif        
InBlock.gif    0,0,0,1,0,2,-1,1                
InBlock.gif        
InBlock.gif    }                    
InBlock.gif};    
 
这个数组的具体含义我已经在思路里讲的很清楚了,这里就不再赘述了。
4.         画小方块
画小方块不是可以直接调用 Rectangle HDC hdc,int x1,int y1,int x2,int y2 )函数不就可以 了嘛,为什么还要自己动手写呢。因为我们画小方块时不需要知道小方块的具体坐标,只要知道小方块左上角的坐标对游戏区左上角坐标的偏移量就可以了。我们用下面的函数来实现。
InBlock.gif void DrawRectangle(HDC hdc, int shape, int x, int y)
InBlock.gif{
InBlock.gif  HBRUSH brush;
InBlock.gif   int leftx=GameSizeX+SquareSize*x;
InBlock.gif   int lefty=GameSizeY+SquareSize*y;
InBlock.gif   int rightx=leftx+SquareSize;
InBlock.gif   int righty=lefty+SquareSize;
InBlock.gif  brush=CreateSolidBrush(color[shape]);
InBlock.gif  SelectObject(hdc,brush);
InBlock.gif  Rectangle(hdc,leftx,lefty,rightx,righty);
InBlock.gif  DeleteObject(brush);
InBlock.gif}
其中 x y 分别表示小方块左上角横、纵坐标相对于游戏区左上角横、纵坐标的偏移量。 X 的范围为 [0,9] y 的范围为 [0,19]
5.         画俄罗斯方块
InBlock.gif void DrawTerics(HDC hdc, int shape, int dir, int x, int y)
InBlock.gif{
InBlock.gif   int index;
InBlock.gif  RECT rect;
InBlock.gif   for(index=0;index<4;index++)
InBlock.gif  {
InBlock.gif    nx[index]=Terics[shape][dir][index].x+x;
InBlock.gif    ny[index]=Terics[shape][dir][index].y+y;
InBlock.gif    rect.left=nx[index];
InBlock.gif    rect.top=ny[index];
InBlock.gif    rect.right=nx[index]+SquareSize;
InBlock.gif    rect.bottom=ny[index]+SquareSize;
InBlock.gif    DrawRectangle(hdc,shape,nx[index],ny[index]);
InBlock.gif  }
InBlock.gif}
 
6.         判断范围
InBlock.gifBOOL IsOutOfRegion( int shape, int dir, int x, int y)
InBlock.gif{
InBlock.gif   int index;
InBlock.gif   int nx,ny;
InBlock.gif   for(index=0;index<4;index++)
InBlock.gif  {
InBlock.gif    nx=Terics[shape][dir][index].x+x;
InBlock.gif    ny=Terics[shape][dir][index].y+y;
InBlock.gif     if(nx<0||nx>=GameRegionWidth||ny<0||ny>=GameRegionHeight||
InBlock.gifGameMapStatesInfo[nx][ny].states!=0)
InBlock.gif    {
InBlock.gif       return TRUE;
InBlock.gif    }
InBlock.gif  }
InBlock.gif   return FALSE;
InBlock.gif    
InBlock.gif}
 
7.         保存游戏区的状态及消去一行
InBlock.gif void SaveStateOfTerics( int shape, int dir, int x, int y)                    
InBlock.gif{        
InBlock.gif   int index;
InBlock.gif   int nx,ny;    
InBlock.gif         int indexX,indexY;
InBlock.gif  BOOL flag;
InBlock.gif   int m,n;
InBlock.gif   for ( index = 0; index < 4; ++index)                
InBlock.gif  {        
InBlock.gif    nx=Terics[shape][dir][index].x+x;
InBlock.gif    ny=Terics[shape][dir][index].y+y;
InBlock.gif         GameMapStatesInfo[nx][ny].states = 1;    
InBlock.gif    GameMapStatesInfo[nx][ny].shapes=shape;
InBlock.gif  }    
InBlock.gif   for(indexX=0;indexX<GameRegionHeight;indexX++)
InBlock.gif  {
InBlock.gif    flag=TRUE;
InBlock.gif     for(indexY=0;indexY<GameRegionWidth;indexY++)
InBlock.gif    {
InBlock.gif       if(GameMapStatesInfo[indexY][indexX].states!=1)
InBlock.gif      {
InBlock.gif        flag=FALSE;
InBlock.gif      }
InBlock.gif    }
InBlock.gif     if(flag)
InBlock.gif    {
InBlock.gif       for(m=indexX;m>=1;--m)
InBlock.gif      {
InBlock.gif         for(n=0;n<GameRegionWidth;++n)
InBlock.gif        {
InBlock.gif          GameMapStatesInfo[n][m].states=GameMapStatesInfo[n][m-1].states;
InBlock.gif          GameMapStatesInfo[n][m].shapes=GameMapStatesInfo[n][m-1].shapes;
InBlock.gif        }
InBlock.gif      }
InBlock.gif      ++count;
InBlock.gif    }
InBlock.gif  }
InBlock.gif}
 
我将两个功能写在了同一个函数里,这样实现起来比较简单。
8.         显示图形
这里需要用到擦除函数和加载函数。我利用的是擦除背景法,这个方法好用,但是实际
运用起来会发现其中的问题,就是刷新太频繁,屏幕有“闪动”的现象。大家可以试一下其他方法。例如我在思路里提到的第一种方法。
定义擦除函数:
InBlock.gif void EraseGameRegion(HDC hdc)
InBlock.gif{
InBlock.gif  RECT rect;
InBlock.gif  HBRUSH brush;
InBlock.gif  rect.left=GameSizeX;
InBlock.gif  rect.top=GameSizeY;
InBlock.gif  rect.right=GameSizeX+GameRegionWidth*SquareSize;
InBlock.gif  rect.bottom=GameSizeY+GameRegionHeight*SquareSize;
InBlock.gif  brush=CreateSolidBrush(RGB(255,255,255));
InBlock.gif  FillRect(hdc,&rect,brush);
InBlock.gif  DeleteObject(brush);
InBlock.gif}
 
定义加载函数:
InBlock.gif void LoadCurrentTerics(HDC hdc)
InBlock.gif{
InBlock.gif int indexX,indexY;
InBlock.gif for(indexX=0;indexX<GameRegionWidth;indexX++)
InBlock.gif{
InBlock.gif   for(indexY=0;indexY<GameRegionHeight;indexY++)
InBlock.gif  {
InBlock.gif     if(GameMapStatesInfo[indexX][indexY].states==1)
InBlock.gif    {
InBlock.gif        DrawRectangle(hdc,GameMapStatesInfo[indexX][indexY].shapes,indexX,indexY);
InBlock.gif    }
InBlock.gif  }
InBlock.gif}
InBlock.gif}
 
定义显示函数:
InBlock.gif void PrintInfo(HDC hdc, int *shape)
InBlock.gif{
InBlock.gif char szscore[200]= "score:";
InBlock.gif char strscore[20];
InBlock.gifscore=count*10;
InBlock.gifitoa(score,strscore,10);
InBlock.gifstrcat(szscore,strscore);
InBlock.gifSetBkColor(hdc,RGB(165,180,255));
InBlock.gifTextOut(hdc,600,100,szscore,strlen(szscore));
InBlock.gifDrawTerics(hdc,*shape,0,15,5);
InBlock.gif}
9.         初始化问题
    俄罗斯方块中的方块每次都是从固定的地方出来的,因此我定义了一个初始化函数,让方块每次从固定的地方出来,且每次落下的方块形状是随机的。
 
InBlock.gif struct CurrentTerics
InBlock.gif{
InBlock.gif int shape,dir,x,y;
InBlock.gif}CurrentTericsInfo;
InBlock.gif void InitTericsInfo()
InBlock.gif{        
InBlock.gif int *p=szshape;
InBlock.gifsrand((unsigned)time(NULL));
InBlock.gifCurrentTericsInfo.shape=rand()%7;
InBlock.gifCurrentTericsInfo.dir=0;
InBlock.gifCurrentTericsInfo.x=4;
InBlock.gifCurrentTericsInfo.y=0;
InBlock.gif*p++=CurrentTericsInfo.shape;
InBlock.gif}
这个函数中用到了一个指针,主要是为了用来保存每次落下的方块的形状,游戏区右边有一个地方时用来显示下一个要落下的方块,这个功能还有待改进,显示的是和当前图形的形状一样的图形,而不是下一个要落下的图形。这个问题有待解决,希望你自己可以解决哦。
10.         颜色问题
俄罗斯方块是有颜色的,我让每一种形状的方块对应一种颜色,且每种形状的颜色是一
样的。所以我们还需要一个表示 7 中颜色的数组。这 7 种颜色当然是自己选定的,可以是任意的颜色,依自己喜欢而定。
InBlock.gif const COLORREF color[7] =        
InBlock.gif{        
InBlock.gif  RGB(255,0,0),        
InBlock.gif  RGB(240,100,5),        
InBlock.gif  RGB(150,250,60),        
InBlock.gif  RGB(27,229,50),        
InBlock.gif  RGB(10,125,145),        
InBlock.gif  RGB(20,12,184),        
InBlock.gif  RGB(116,34,156)        
InBlock.gif        
InBlock.gif};    
 
11.         游戏结束
InBlock.gifBOOL IsGameOver( int shape, int dir, int x, int y)
InBlock.gif{
InBlock.gif   int index;
InBlock.gif   int nx,ny;
InBlock.gif   for(index=0;index<4;index++)
InBlock.gif  {
InBlock.gif    nx=Terics[shape][dir][index].x+x;
InBlock.gif    ny=Terics[shape][dir][index].y+y;
InBlock.gif     if(ny==0 && GameMapStatesInfo[nx][ny].states==1)
InBlock.gif    {
InBlock.gif       return TRUE;
InBlock.gif    }
InBlock.gif  }
InBlock.gif   return FALSE;
InBlock.gif
InBlock.gif}
 
下面就剩下组合问题了,要把这些单独的函数组合在一起,我会在其他文章中给出完整的代码。