简单的MFC俄罗斯方块

说明

这是使用 Visual Studio 2013的MFC单文档 开发的一个 简单的 俄罗斯方块游戏。适合初学者,源码中包含 大量注释 ,下载文件中包含 XMind思维导图 辅助理解。

MFC俄罗斯方块 - 源码下载


游戏功能介绍

===== RBox_1.0 功能实现 =====
1.开始游戏
2.方块的创建
3.下一方块的预览
4.方块的自由下落
5.方块的碰撞检测(包含变形时碰撞检测)
6.键盘控制方块的移动
7.游戏结束判定
8.方块下落速度随得分加快

===== RBox_1.1 功能更新 =====
1.添加了游戏暂停
2.添加了按下数字键改变下一方块类型的功能


游戏界面

在这里插入图片描述


游戏数据

#define MAX_ROW 22	//设置行数为22
#define MAX_COL 12	//设置列数为12
#define BOX_SIZE 30	//设置方块大小为30
#define MIN_ROW_COL 4	//设置下一方块、下落方块在4行4列方块中
#define KEY_UP 0
#define KEY_DOWN 1
#define KEY_LEFT 2
#define KEY_RIGHT 3

//所有成员变量
bool m_boxMap[MAX_ROW][MAX_COL];	//地图方块
bool m_boxNow[MIN_ROW_COL][MIN_ROW_COL];	//下落方块
bool m_boxWill[MIN_ROW_COL][MIN_ROW_COL];	//下一方块
CPoint m_potNow;	//下落方块的左上点坐标
bool m_bisEnd;	//游戏结束标志
int m_iScore;	//游戏得分
int m_iSpeed;	//下落速度
bool m_bisGaming; //是否在游戏中

核心代码

● 创建方块

void CRBoxView::boxCreate()		//赋值下落方块,产生下一方块
{
	int iRow, iCol;
	
	for (iRow = 0; iRow < MIN_ROW_COL; iRow++)
	{
		for (iCol = 0; iCol < MIN_ROW_COL; iCol++)
		{
			m_boxNow[iRow][iCol] = m_boxWill[iRow][iCol];
			m_boxWill[iRow][iCol] = false;
		}
	}

	srand((unsigned)time(0));
	int iType = rand() % 4;		//四种方块,其中L型有两种

	switch (iType)
	{
	case 0:
		m_boxWill[0][0] = 1;	// ■ □ □ □
		m_boxWill[1][0] = 1;	// ■ □ □ □
		m_boxWill[2][0] = 1;	// ■ □ □ □	
		m_boxWill[3][0] = 1;	// ■ □ □ □
		break;
	case 1:
		m_boxWill[0][0] = 1;	// ■ ■ □ □
		m_boxWill[0][1] = 1;	// ■ □ □ □
		m_boxWill[1][0] = 1;	// ■ □ □ □
		m_boxWill[2][0] = 1;	// □ □ □ □
		break;
	case 2:
		m_boxWill[0][0] = 1;	// ■ ■ □ □
		m_boxWill[1][1] = 1;	// □ ■ □ □
		m_boxWill[0][1] = 1;	// □ ■ □ □
		m_boxWill[2][1] = 1;	// □ □ □ □
		break;
	case 3:
		m_boxWill[0][0] = 1;	// ■ ■ □ □
		m_boxWill[0][1] = 1;	// ■ ■ □ □
		m_boxWill[1][0] = 1;	// □ □ □ □
		m_boxWill[1][1] = 1;	// □ □ □ □
		break;

	default:
		break;
	}

	//重置下落方块出现的位置
	m_potNow.x = 0;
	m_potNow.y = MAX_COL / 2;
}

● 方块移动

void CRBoxView::boxMove(int iKey)
{
	if (m_bisGaming)
	{
		bool bisHit = false;//碰撞状态

		switch (iKey)
		{
		case KEY_UP:
			boxHitJudge(m_boxNow, KEY_UP, m_potNow);
			break; 

		case KEY_DOWN:
			bisHit = boxHitJudge(m_boxNow, KEY_DOWN, m_potNow);
			if (bisHit == true)		//若为下边界碰撞
			{	
				boxLineDel();			//判断消行
				boxCreate();		//产生新下落方块和下一方块
				m_bisEnd = gameOver();	//游戏是否结束
			}
			break;

		case KEY_LEFT:
			boxHitJudge(m_boxNow, KEY_LEFT, m_potNow);
			break;

		case KEY_RIGHT:
			boxHitJudge(m_boxNow, KEY_RIGHT, m_potNow);
			break;

		default:
			break;
		}
	}
}

● 碰撞检测

bool CRBoxView::boxHitJudge(bool boxTmp[4][4], int iKey, CPoint & p)
{
	int iRow, iCol;

	//清空下落方块经过的路径
	for (iRow = 0; iRow < MIN_ROW_COL; iRow++)
	{
		for (iCol = 0; iCol < MIN_ROW_COL; iCol++)
		{
			if (boxTmp[iRow][iCol] == true)
			{
				m_boxMap[p.x + iRow][p.y + iCol] = false;	//把原下落方块擦除
			}
		}
	}

	//碰撞检测
	for (iRow = 0; iRow < MIN_ROW_COL; iRow++)
	{
		for (iCol = 0; iCol < MIN_ROW_COL; iCol++)
		{
			if (boxTmp[iRow][iCol] == true)
			{
				switch (iKey)
				{
				case KEY_UP:
					{
						bool bisRoll = boxRoll(boxTmp, p);
						if (bisRoll == true)	//可以旋转
							return false;		//返回未发生碰撞
					}break;

				case KEY_DOWN:
					{
						if ((p.x + iRow + 1) >= MAX_ROW)	//与底边界碰撞
							goto HIT_TRUE;
						if (m_boxMap[p.x + iRow + 1][p.y + iCol] == true)	//与地图上堆积的方块碰撞
							goto HIT_TRUE;
					}break;

				case KEY_LEFT:
					{
						if ((p.y + iCol - 1) < 0)	//与左边界碰撞
							goto HIT_TRUE;
						if (m_boxMap[p.x + iRow][p.y + iCol - 1])	//与地图上堆积的方块碰撞
							goto HIT_TRUE;
					}break;

				case KEY_RIGHT:
					{
						if ((p.y + iCol + 1) >= MAX_COL)	//与右边界碰撞
							goto HIT_TRUE;
						if (m_boxMap[p.x + iRow][p.y + iCol + 1])	//与地图上堆积的方块碰撞
							goto HIT_TRUE;
					}break;

				default:
					break;
				}
			}
		}
	}

	switch (iKey)
	{
	case KEY_UP:
		break;
	case KEY_DOWN:
		p.x++;
		break;
	case KEY_LEFT:
		p.y--;
		break;
	case KEY_RIGHT:
		p.y++;
		break;

	default:
		break;
	}

	//未发生碰撞,下落方块位置变动后更新到地图上
	for (iRow = 0; iRow < MIN_ROW_COL; iRow++)
	{
		for (iCol = 0; iCol < MIN_ROW_COL; iCol++)
		{
			if (boxTmp[iRow][iCol] == true)
			{
				m_boxMap[p.x + iRow][p.y + iCol] = true;
			}
		}
	}
	return false;	//返回未发生碰撞

//发生碰撞,下落方块位置不变的更新到地图上
HIT_TRUE:
	for (iRow = 0; iRow < MIN_ROW_COL; iRow++)
	{
		for (iCol = 0; iCol < MIN_ROW_COL; iCol++)
		{
			if (boxTmp[iRow][iCol] == true)
			{
				m_boxMap[p.x + iRow][p.y + iCol] = true;
			}
		}
	}
	return true;	//返回发生碰撞
}

● 方块旋转

bool CRBoxView::boxRoll(bool boxTmp[4][4], CPoint & p)
{
	bool boxPre[MIN_ROW_COL][MIN_ROW_COL];		//暂存存放
	bool boxFin[MIN_ROW_COL][MIN_ROW_COL];		//完成旋转

	int iMinRow = MIN_ROW_COL;
	int iMinCol = MIN_ROW_COL;
	int iRow, iCol;

	bool bisRightOver = false;	//超过右边界
	int iRightOver = 0;		//用于记录超出有边界的列数

	if (p.x >= 4)	//下落方块完全进入地图界面
	{
		for (iRow = 0; iRow < MIN_ROW_COL; iRow++)				// ■ ■ □ □						□ □ □ □			
		{														// ■ □ □ □		逆时针旋转->	    □ □ □ □
			for (iCol = 0; iCol < MIN_ROW_COL; iCol++)			// ■ □ □ □						■ □ □ □
			{													// □ □ □ □						■ ■ ■ □
				boxPre[iRow][iCol] = boxTmp[iCol][3 - iRow];	// 将方块逆时针旋转90°给暂存数组
				boxFin[iRow][iCol] = false;
			}
		}

		for (iRow = 0; iRow < MIN_ROW_COL; iRow++)
		{
			for (iCol = 0; iCol < MIN_ROW_COL; iCol++)
			{
				if (boxPre[iRow][iCol] == true)
				{
					if (iRow < iMinRow)
						iMinRow = iRow;
					if (iCol < iMinCol)
						iMinCol = iCol;
				}
			}
		}
		for (iRow = iMinRow; iRow < MIN_ROW_COL; iRow++)						// □ □ □ □					■ □ □ □
		{																		// □ □ □ □		移动->		■ ■ ■ □
			for (iCol = iMinCol; iCol < MIN_ROW_COL; iCol++)					// ■ □ □ □					□ □ □ □
			{																	// ■ ■ ■ □					□ □ □ □
				boxFin[iRow - iMinRow][iCol - iMinCol] = boxPre[iRow][iCol];	// 将暂存数组中的方块移至左上角 给完成数组
			}
		}

		for (iRow = 0; iRow < MIN_ROW_COL; iRow++)
		{
			for (iCol = 0; iCol < MIN_ROW_COL; iCol++)
			{
				if (boxFin[iRow][iCol] == false)
					continue;
				if ((p.x + iRow) >= MAX_ROW)	//碰撞地图下边界
					return false;
				if (m_boxMap[p.x + iRow][p.y + iCol] == true)	//碰撞到地图上原本存在的方块
					return false;
				if ((p.y + iCol) >= MAX_COL)	//超过右边界
				{
					bisRightOver = true;
					iRightOver = p.y + iCol + 1 - MAX_COL;	//记录超出右边界多少
				}
			}
		}
	   																			//	 右边界
																				//     ||
		if (bisRightOver == true)	//超出右边界的情况的处理					    // ■ ■ || □ □      ■ □ || □ □	   ■ □ □ || □
		{																		// ■ □ || □ □  ->  ■ ■ || ■ □  ->  ■ ■ ■ || □
			p.y -= iRightOver;		//将方块向左移动使其不超出右边界		    	// ■ □ || □ □	   □ □ || □ □	   □ □ □ || □
		}																		// □ □ || □ □	   □ □ || □ □	   □ □ □ || □

		for (iRow = 0; iRow < MIN_ROW_COL; iRow++)
		{
			for (iCol = 0; iCol < MIN_ROW_COL; iCol++)
			{
				boxTmp[iRow][iCol] = boxFin[iRow][iCol];
			}
		}
		return true;	//旋转成功
	}
	else
		return false;
}

● 消行

void CRBoxView::boxLineDel()
{
	int iRow, iCol;
	bool bisDel;	//是否可消行

	for (iRow = 0; iRow < MAX_ROW; iRow++)
	{
		bisDel = true;

		for (iCol = 0; iCol < MAX_COL; iCol++)
		{

			if (m_boxMap[iRow][iCol] == false)	//当前行存在空方块就不可消行
			{		
				bisDel = false;			
			}
		}

		if (bisDel == true)		//当前行可消
		{	
			for (int iRowTmp = iRow; iRowTmp > 0; iRowTmp--)
			{
				for (int iColTmp = 0; iColTmp < MAX_COL; iColTmp++)
				{
					//将当前行以上的方块都向下移动一行
					m_boxMap[iRowTmp][iColTmp] = m_boxMap[iRowTmp - 1][iColTmp];	
				}
			}
			for (int iFirst = 0; iFirst < MAX_COL; iFirst++)
			{
				m_boxMap[0][iFirst] = false;		//手动清空第一行
			}

			m_iScore +=100;		//消行记分

			//游戏难度增加
			switch (m_iScore/100)	
			{
			case 10:
				m_iSpeed = 300;
				break;
			case 20:
				m_iSpeed = 200;
				break;
			case 30:
				m_iSpeed = 100;

			default:
				break;
			}
			SetTimer(1, m_iSpeed, NULL);
		}
	}
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页