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