MFC俄罗斯方块设计报告
使用MFC设计一款俄罗斯方块。实现方块下落,方块移动,方块叠加,方块变形,消行等功能。
MFC双缓冲绘图,方块移动,下落,叠加,变形,消行处理。
如附录图2-1俄罗斯方块运行流程图所示。
游戏类的成员函数
bool gameOver();//游戏结束判断
void rectLineDel();//满行进行消行处理
bool rectChange(bool bufTmp[][4],CPoint &pot);//方块变形处理
void rectMove(int iDirect);//方块移动
bool hitJudge(bool bufTmp[][4],int iDirect,CPoint &pot);//方块碰撞判断,包含移动处理
void willRectProduce();//下一个方块随机产生
void fontDraw(CDC* pCDC);//绘制游戏界面的文字
void willRectDraw(CDC *pCDC);//绘制将要出现的方块
void mapDraw(CDC * pCDC);//绘制游戏的地图
void gameStart();//开始游戏
游戏类的成员变量
bool m_bEndFlg;//游戏结束判断
CPoint m_potNow;//当前掉下方块的左上点的位置
int m_iScore;//分数
bool m_bufMap[MAX_ROW][MAX_COL];//游戏的地图
bool m_bufNow[MIN_ROW_ROL][MIN_ROW_ROL];//当前这在掉落的方块
bool m_bufWill[MIN_ROW_ROL][MIN_ROW_ROL];//将要出现的方块
说明:后面所说的块是指4*4的二维数组,地图块是指整张游戏的背景。
所有实现是通过绘制一个一个的方块实现的。
//绘制游戏的背景,正在下落的,和已经堆积的
void CTetrisGame::mapDraw(CDC *pCDC)
{
int iRow = 0;
int iCol = 0;
CPen penMap;
penMap.CreatePen(PS_SOLID,1,RGB(0,0,0)); //定义白色画笔绘制蛇的边框,第一个参数代表
pCDC->SelectObject(&penMap);
for ( iRow = 4; iRow < MAX_ROW; iRow++)//初始化地图,全部置0
{
for( iCol = 0; iCol < MAX_COL; iCol++)
{
if (m_bufMap[iRow][iCol] == FALSE)
{
CBrush brushMap;//这里要特别注意,虽然两次是不同的颜色,但是要分别载入画刷
brushMap.CreateSolidBrush(RGB(255,255,255));
pCDC->SelectObject(&brushMap);
pCDC->Rectangle(CRect(iCol*RECT_SIZE,(iRow-4)*RECT_SIZE,(iCol+1)*RECT_SIZE,(iRow+1-4)*RECT_SIZE));
}
if(m_bufMap[iRow][iCol] == TRUE)
{
CBrush brushMap;
brushMap.CreateSolidBrush(RGB(0,255,255)); //蓝色为背景
pCDC->SelectObject(&brushMap);
pCDC->Rectangle(CRect(iCol*RECT_SIZE,(iRow-4)*RECT_SIZE,(iCol+1)*RECT_SIZE,(iRow+1-4)*RECT_SIZE));
}
}
}
}
通过扫描地图二维数组m_bufMap的每个元素的值,根据真假分别填充不同颜色的画刷。实际上每个方块掉落,并不是绘制单独绘制每个方块,而是根据掉落方块的m_bufNow的真值,以及当前掉落块的最左上角的坐标m_potNow,实时赋值给m_bufMap,掉落块走过的区域,又会进行清零处理,直到m_bufNow不动了。全部的过程绘制都是在扫描m_bufMap进行绘制。
主要为几步:
1.把上一次产生m_bufWill赋值m_bufNow
2.上一次产生m_bufWill清零
3.对生成的随机数对7进行取余,得到将要生成的m_bufWill
4.初始化当前掉落快最左上角的坐标m_potNow,变为为第一行,中间列
m_potNow.x = 0;//ÖØж¨ÒåµôÂä·½¿éµÄµã
m_potNow.y = MAX_COL/2;
说明:在初始化时要调用willRectProduce( )两次,因为初始化时还没有产生m_bufWill;在当前块落地后,还用调用willRectProduce( )一次,产生新的下落块。
函数bool CTetrisGame::hitJudge(bool bufTmp[][4], int iDirect, CPoint &pot)主要做掉落块与地图块碰撞检测,当前有碰撞发生,返回真,当没碰撞发生,根据方向移动坐标m_potNow。主要为下面几步:
1.扫描下落块为真点,把地图块上相应点清零:实现下落块轨迹擦除
2.扫描下落块为真点,结合当前m_potNow,和移动方向,判断下落块与左右下边界以及地图块上真点是否重合:若重合,说明不可移动,跳转到HIT_TRUE执行
case DIR_DOWN://
{
if ((pot.x+iRow+1) >= MAX_ROW )//µ±µãµÄºá×ø±ê+1³¬¹ý×îÏÂÃæµÄʱºò
goto HIT_TRUE;
if ( m_bufMap[pot.x+iRow+1][pot.y+iCol] == TRUE)
goto HIT_TRUE;
}break;
case DIR_LEFT:
{
if ((pot.y+iCol-1)<0)//˵Ã÷Åöµ½×ó±ß½çÁË
goto HIT_TRUE;
if ( m_bufMap[pot.x+iRow][pot.y+iCol-1] == TRUE)
goto HIT_TRUE;
}break;
case DIR_RIGHT:
{
if ((pot.y+iCol+1)>=MAX_COL)//˵Ã÷Åöµ½Óұ߽çÁË
goto HIT_TRUE;
if ( m_bufMap[pot.x+iRow][pot.y+iCol+1] == TRUE)//ÕâÀï¶à´ÖÐÄ´òÒ»¸ö¼ÓºÅ£¬ÒªÐ¡ÐÄ£¡
goto HIT_TRUE;
}break;
3.若没有发生碰撞,结合方向,把坐标m_potNow做出相应的方向+1,再把方向块真点结合变化后的m_potNow赋值给地图块,就这样产生移动,视觉上看上就是下落块在移动,其实是在重绘地图块发生的“错觉”。
for ( iRow = 0; iRow < MIN_ROW_ROL; iRow++)//ûÓÐÅöײ·¢Éúʱ£¬°Ñµ±Ç°µÄ×ø±ê+1¸³Öµ¸øµØͼ
{
for ( iCol = 0; iCol < MIN_ROW_ROL; iCol++ )
{
if (bufTmp[iRow][iCol] == TRUE)
{
m_bufMap[pot.x+iRow][pot.y+iCol] = TRUE;
}
}
}
4.若发生碰撞,执行HIT_TRUE:与上面的区别是m_potNow不会变化,其他一样。但是向下掉落的方向是一直被定时器调用的。所以即使不能左右平移还会往下掉。
当按下向上方向键时,调用rectChange函数。主要为下面几步:
1.初始化最终变化块bufAfter,全部置为FALSE,把下落块逆时针旋转90°赋值给中间块bufMid;
for ( iRow = 0; iRow < MIN_ROW_ROL; iRow++)
{
for ( iCol = 0; iCol < MIN_ROW_ROL; iCol++)
{
bufMid[iRow][iCol] = bufTmp[iCol][3-iRow];//°Ñµ±Ç°ÏÂÂänow·½¿éÄæʱÕëÐýת90¡ã¸øÖмäÔÝ´æÊý×é
bufAfter[iRow][iCol] = FALSE;//ÐýתºóµÄÊý×éÏÈÈ«²¿ÖÃÁã
}
}
2.找到bufMid为真的最小行和列,即块中有填充的点的最小行号和列号;
3.根据找到的bufMid最小行列对变换后的矩阵平移到左上角,赋值给bufAfter;
for ( iRow = iRowMin; iRow < MIN_ROW_ROL; iRow++)
{
for ( iCol = iColMin; iCol < MIN_ROW_ROL; iCol++)
{
bufAfter[iRow-iRowMin][iCol-iColMin] = bufMid[iRow][iCol];//°Ñ±ä»»ºóµÄmidÊý×éƽÒƵ½×óÉϽǣ¬³ÉΪafterÊý×é
}
}
4.bufAfter块真点结合m_potNow,与地图块进行比较,如果重合说明不能变形,返回FALSE;
5.若能够变形,把bufAfter赋值给m_bufNow,实现下落块的变形,返回真。
地图块上某一行全满了,要进行消行处理,即该行上一行的信息全部赋值给该行,依次类推,直到第0行。
for ( iRow = 0; iRow < MAX_ROW; iRow++)
{
bLineDelFlg = TRUE;
for (iCol = 0; iCol < MAX_COL; iCol++)
{
if ( m_bufMap[iRow][iCol] == FALSE)
bLineDelFlg = FALSE; //ÈôÒ»ÐÐÖÐÓÐûÂúµÄ£¬°Ñ±ê־λÖÃ0
}
if (bLineDelFlg == TRUE)//ÏûÐбê־Ϊ1
{
for ( int iRowTmp = iRow; iRowTmp > 0; iRowTmp--)
{
for (int iColTmp = 0; iColTmp < MAX_COL; iColTmp++)
{
m_bufMap[iRowTmp][iColTmp] = m_bufMap[iRowTmp-1][iColTmp];
}
}
for ( int iFirst = 0; iFirst < MAX_COL; iFirst++)
{
m_bufMap[0][iFirst] = FALSE;
}
m_iScore++;
}
}
这里根据按键按下的方向,进行相应的处理。
//·½¿éµÄÒƶ¯º¯Êý
void CTetrisGame::rectMove(int iDirect)
{
int iHitState = -1;
switch (iDirect)
{
case DIR_UP:
{
hitJudge(m_bufNow,DIR_UP,m_potNow);
}break;
case DIR_DOWN:
{
iHitState = hitJudge(m_bufNow,DIR_DOWN,m_potNow);//µÃµ½ÅöײµÄ״̬£¬²¢ÇÒÈç¹ûûÓÐÅöײ£¬»á·¢ÉúÒƶ¯º¯Êý
if ( iHitState == TRUE)//Èç¹ûµ×²¿ÊÇ·¢ÉúÅöײ
{
rectLineDel();//ÿ´Îµ×²¿ÂäµØºó½øÐÐÊÇ·ñÏû³ýÐеÄÅжÏ
willRectProduce();//µ×²¿ÂäµØºóÒª½øÐвúÉúеķ½¿é£¬²¢ÇÒm_potNowµÄλÖÃÒª³õʼ»¯
m_bEndFlg = gameOver();//ÿ´ÎÂäµØºóÒª½øÐÐÓÎÏ·ÊÇ·ñ½áÊøµÄÅжÏ
break;
}
}break;
case DIR_LEFT:
{
hitJudge(m_bufNow,DIR_LEFT,m_potNow);
}break;
case DIR_RIGHT:
{
hitJudge(m_bufNow,DIR_RIGHT,m_potNow);
}break;
}
}
说明:下落方向移动时,如果落地,要进行消行判断处理,产生下一个m_bufWill,是否游戏结束判断处理。当按下向上方向键时,在碰撞函数hitJudge中会是否进行rectChange处理。
当向下着地后进行是否游戏结束判断。
//ÓÎÏ·½áÊø
bool CTetrisGame::gameOver()
{
int iRow = 0;
int iCol = 0;
for ( iCol = 0; iCol < MAX_COL; iCol++)
{
if ( m_bufMap[2][iCol] == TRUE)
return TRUE;
}
return FALSE;
}
http://download.csdn.net/detail/luoyikun/8223053
源程序下载
点击打开链接