俄罗斯方块 c++ MFC

一、实验实习目的及要求
加强对C++语言的深入理解,达到提高学生分析问题,解决综合问题的能力。要求:分析俄罗斯方块游戏的类及层次图;实现俄罗斯方块游戏的基本功能;实现界面:Windows界面,利用对话框应用程序形式。
二、实验实习设备(环境)及要求(软硬件条件)
设备:PC机
操作系统:Windows
编译软件:Visual Studio 2019
三、实验实习内容简介:
系统应实现的主要功能:
游戏方块移动,游戏方块的碰撞检测,游戏方块旋转,满行消除,分数以及等级计算,开始、结束以及重新开始按钮,游戏方块预览功能。
俄罗斯方块游戏的主要执行过程如下图:

在这里插入图片描述
界面设计及绘制:

在这里插入图片描述

类图:
在这里插入图片描述

各模块的具体功能和简单算法:
1、小方块
每个俄罗斯方块由四个小方块组成。定义小方块类,实现绘制,移动,擦除功能。
2、小方块组合
定义小方块组合即俄罗斯方块类,实现俄罗斯方块的绘制,移动,擦除功能。
3、定时机制
建立计时器后,根据参数定义的时间步长触发定时处理时间,直到杀死计时器为止。通过计时器实现,每隔固定时间,检测方块状态以及方块下落,预览下一方块。当游戏结束或暂停时杀死计时器。
4、绘图实现
游戏开始时初始化界面,绘制俄罗斯方块的网状图、分数以及等级显示。
5、边界判断
通过键盘响应消息处理函数,响应键盘操作,判断俄罗斯方块是否与边界或者下方方块碰撞。
6、方块旋转功能
将小方块组合的位置基点作为旋转中心点,通过旋转各个小方块中心位置到小方块组合的位置基点的x与y边实现俄罗斯方块的旋转。
7、满行消除功能
将俄罗斯方块运动的主要界面看作一个二维数组,存储颜色。当某行满时,消除该行的方块,并且将该行上方的方块颜色向下移一行。
8、方块预览功能
生成的方块存储在数组上,在第一次生成方块时,生成两个方块,第一个方块显示在俄罗斯方块运动界面上,第二个方块预览。后面时运动方块的倒数第二个俄罗斯方块,预览倒数第一个方块。

四、源程序(代码)
1、定时机制
void CMFCADlg::OnTimer(UINT_PTR nIDEvent)
{
//UpdateData(TRUE);
//CDC* pDC = GetDlgItem(IDC_GRID)->GetWindowDC();
CDC *pDC = this->GetDC();//获取设备上下文对象
CCombine C;
if (combines.size() == 0 )
{
C=CreateCombine(pDC);
C.CCombine::Draw(pDC);
CreateCombine(pDC);
NextCombine();
Gameon = true;
}

vector<CCombine>::iterator p = combines.end() - 2;//获取方块组合最后一个对象
if (isUnder() == false && Gameon == true)
{
	p->CCombine::Draw(pDC);
	p->CCombine::Wipe(pDC);
	p->CCombine::Move(pDC, 0, 40);
}
else if (Gameon == true )
{
	int game = true;
	//UpdateTop(pDC);
	for (unsigned int i = 0; i < p->squares.size(); i++)//普通遍历
	{
		//游戏结束

		if (p->squares[i].center.y == 20)
		{
			
			
			CString str;
			str = TEXT("sorry, Game is over,Please try again");
			Gameover();
			if (MessageBox(str, TEXT("INFO"), MB_ICONINFORMATION) == IDOK) {
				OnBnClickedButton1();
				
			}
			game = false;
			break;
		}
		fflag[(p->squares[i].center.y) / 40][(p->squares[i].center.x) / 40] = p->squares[i].color;
	}
	if (game)
	{
		p->CCombine::Wipe(pDC);
		p->CCombine::Draw(pDC);

		LineClear(pDC);

		CreateCombine(pDC);
		C = combines[combines.size() - 2];
		C.CCombine::Draw(pDC);
		NextCombine();
	}
	
	
	
}
//ReleaseDC(pDC);
CDialogEx::OnTimer(nIDEvent);

}
2、方块预览功能
}
void CMFCADlg::NextCombine()
{
CDC* pDC = this->GetDC();//获取设备上下文对象

COLORREF color;
color = RGB(245, 245, 245);
CBrush newBrush;         // 创建的新画刷   
CBrush* pOldBrush;       // 旧画刷的指针
newBrush.CreateSolidBrush(color);
pOldBrush = pDC->SelectObject(&newBrush);
CRect rect(600, 10,600+240,200  );

pDC->FillRect(&rect, &newBrush);//CDC::FillSolidRect
// 恢复旧画刷   
pDC->SelectObject(pOldBrush);
// 删除新画刷   
newBrush.DeleteObject();
CCombine temp;
temp = combines[combines.size() - 1];
temp.Move(pDC, 490, 50);
temp.Draw(pDC);

}
3、方块旋转功能
int CMFCADlg::RotateCombines(CDC* pDC)
{
// TODO: 在此处添加实现代码.
vector::iterator p = combines.end() - 2;//获取焦点对象
//x0 = (x - rx0)*cos(a) - (y - ry0)*sin(a) + rx0 ;
//y0 = (x - rx0)*sin(a) + (y - ry0)*cos(a) + ry0;
if (p->pattern != 1 )
{
int preX[10], preY[10], x, y;
int rx = p->location.x;
int ry = p->location.y;
int count = 0;
p->Wipe(pDC);
//遍历计算
bool flag_boundary = false;//判断是否出界,是->不旋转
for (unsigned i = 0; i < p->squares.size(); i++)
{
x = p->squares[i].center.x;
y = p->squares[i].center.y;
//绕location旋转
preX[i] = (x - rx) * cos(-PI * 90 / 180) - (y - ry) * sin(-PI * 90 / 180) + rx;
preY[i] = (x - rx) * sin(-PI * 90 / 180) + (y - ry) * cos(-PI * 90 / 180) + ry;
if (fflag[(preY[i] / 40)][(preX[i] / 40)] == RGB(255, 255, 255))
{
count++;
}
if ((preY[i] > 720) || (preX[i] > 560) || (preY[i] < 20) || (preX[i] < 20))
{
flag_boundary = true;
}
//消除误差,即归位
preX[i] = (preX[i] / 40) * 40 + 20;
preY[i] = (preY[i] / 40) * 40 + 20;
//赋值

	}
	//如果可以旋转,那么赋值
	if (count == p->squares.size() && flag_boundary == false)
	{
		for (unsigned i = 0; i < p->squares.size(); i++)
		{
			p->squares[i].center.x = preX[i];
			p->squares[i].center.y = preY[i];
		}
	}
}
return 0;

}
4、满行消除功能
void CMFCADlg::LineClear(CDC* pDC)//满行消除
{
vector::iterator p = combines.end() - 2;//获取方块组合最后一个对象
COLORREF color;
for (unsigned int i = 0; i < p->squares.size(); i++)
{
//遍历方块所在行
bool flag_fill = true;
COLORREF firstColor = RGB(255,255,255);
for (int j = 20; j < 560; j += 40)
{
color = pDC->GetPixel(j, p->squares[i].center.y);
//如果有颜色不相同的方块,结束
if (color == RGB(255,255,255)||0)
{
flag_fill = false;
break;
}
}
//如果满行,进行消除(当前方块所在行)
if (flag_fill == true)
{
DropPart(p->squares[i].center.y);
}
}
score += 50;
if (score % 200 == 0)
level += 1;
CString str;
str.Format(_T("%d"), score);
Edit_score.SetWindowTextW(str);
str.Format(_T("%d"), level);
Edit_level.SetWindowTextW(str);

}
int CMFCADlg::DropPart(int line)
{
CDC* pDC = this->GetDC();
CRect rect;
int flag=false;
COLORREF color;
for (int j = line; j > 0; j-=40)
{
for (int i = 20; i < 560; i+=40)
{

		if (j - 40 > 0)
		{
			color = pDC->GetPixel(i, j - 40);
			if (color != RGB(255, 255, 255) || 0)
				flag = true;
			rect = CRect(i - 20, j - 20, i + 20, j + 20);
			pDC->FillSolidRect(&rect, color);
			fflag[j/40][i/40] = color;
			flag = true;
			//为矩形画上黑色边框
			CPen NewPen, * oldPen;
			NewPen.CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
			oldPen = pDC->SelectObject(&NewPen);
			pDC->MoveTo(i - 20, j - 20);
			pDC->LineTo(i + 20, j - 20);

			pDC->LineTo(i + 20, j + 20);

			pDC->LineTo(i - 20, j + 20);

			pDC->LineTo(i - 20, j - 20);
			pDC->SelectObject(oldPen);
		}
		
	}
	if (!flag)
		return 0;
}

return 0;

}

五、源程序调试过程(注:该项是实验报告成绩评定的主要评分依据之一,越详细越好,语法错误与逻辑错误都要写出来)
1、cannot seek value-initialized vector iterator
在这里插入图片描述

是因为vector中没有元素却要去取元素,导致的错误。逻辑失误
2、
在这里插入图片描述

函数名前面没有加类名
3、
在这里插入图片描述

ontimer的后面如果调用自己会发生溢出错误
CDialogEx::OnTimer(nIDEvent);
在这里插入图片描述

4、
在这里插入图片描述

如果在多个继承指向成员的指针之间进行强制转换,则可能会发生 C4407。 有时这可以正常工作,但有时不能这样做,因为单个继承指针到成员的表示形式不包含足够的信息。 用 /vmm进行编译可能会有所帮助 你还可以尝试重新排列基类;编译器检测到转换中的信息丢失,因为基类与派生的非零偏移量。
删掉virtual后就没有问题

5、
在生成方块时,在一个模式下漏了squares.push_back()四个方块,导致运行游戏时squares为空而无法运行
在这里插入图片描述

6、
在键盘处理消息中,如果不加这一个语句会导致键盘按下一次响应了两次,比如导致游戏按下一次右键会往右边移动两格
if (pMsg->message == WM_KEYDOWN) //捕捉到键盘

7、
游戏结束时,消息对话框一闪一闪的,因为少了个break,而且执行了之后,ontimer继续执行

五、实验实习结果及分析(程序运行结果截图要求测试用例尽量全面)
移动
在这里插入图片描述

旋转
在这里插入图片描述

消行

在这里插入图片描述
在这里插入图片描述

游戏结束
在这里插入图片描述

方块移动、方块旋转、消行、暂停按钮、开始按钮、重新开始按钮、得分情况、预览情况、游戏结束均通过测试。

七、总结:(通过该项目,你在设计和具体实现环节都学到了什么?是否达到预期结果?有什么不足等?)
在俄罗斯方块项目中,我采用面向对象的方式设计小方块类(Square.h)以及小方块的组合类(Combine.h),采用面向过程的方式编写主要的执行类(MFCADlg.h)。小方块类以及小方块的组合类主要以数据为中心,主要的执行类主要以功能为中心。画出了主要的执行类的实现逻辑图。对类进行设计后,画出了三个类的图。总体采用了瀑布式的开发方式,从上至下开发,以各个功能为模块进行开发以及测试,主要实现每个函数模块执行一个功能,尽量实现高内聚和低耦合的设计思想。在主要的执行类的开发中,先将面板设计图实现,然后依次开发实现小方块组合的初始化,小方块组合的移动以及移动的限制条件,小方块的旋转,消行功能,方块预览功能,开始、暂停、结束按钮的实现,分数和等级的设置。在各个模块的开发中,边测试,边开发。

  • 7
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
俄罗斯方块是一款经典的游戏,多年来一直受到广大玩家的喜爱。MFC(Microsoft Foundation Class)是一套用于Windows编程的C++类库,可以帮助开发者快速创建Windows界面应用程序。 在俄罗斯方块游戏中,我们可以使用多态调用MFC来实现游戏的各种功能。 首先,我们可以创建一个基类TetrisBlock,其中包含俄罗斯方块的基本属性和方法,比如获取方块形状、旋转方块等。 然后,我们可以创建多个继承自TetrisBlock的子类,每个子类代表不同类型的俄罗斯方块,比如I型、T型、L型等。这些子类可以重写基类的方法,以实现自身特定的功能。 接下来,我们可以使用MFC提供的类和方法来创建游戏界面,比如使用CWnd类创建一个窗口,使用CDC类在窗口中绘制游戏界面等。 在游戏进行过程中,我们可以使用多态调用来实现不同类型的俄罗斯方块的移动和旋转。比如,通过创建一个指向基类TetrisBlock的指针,将其指向任意一个子类对象,然后调用指针所指向对象的方法来进行操作。 此外,我们还可以使用MFC提供的类和方法实现用户交互,比如通过CButton类创建按钮,通过CDialog类创建对话框等。这样,玩家就可以使用鼠标或键盘来控制俄罗斯方块的移动、旋转和其他操作。 总之,通过多态调用MFC,我们可以方便地实现俄罗斯方块游戏的各种功能和界面。这样,开发者可以更加专注于游戏的逻辑和用户体验,而无需过多关注底层的实现细节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值