SDUST 小学期飞机大战简述 - 1大体的框架

2020-07-28

  • 版权声明:原创文章,未经博主允许不得转载

这章主要描述下飞机大战的整体架构。这章开始,内容将基于我的 V2.0.0

大体的认识

这里所阐述的过程是通用的,是和老师提供的模板程序一样的,但是代码会有些许不同。

产生一个MFC窗口很容易,先从CWinApp派生一个应用程序类,然后再从这个应用程序类建立应用程序对象(theApp)。两个过程可以容易地在源码中找到:

/*PlaneGame.h line16*/
class CPlaneGameApp : public CWinApp
{
public:
	CPlaneGameApp();

// 重写
public:
	virtual BOOL InitInstance();

// 实现
	afx_msg void OnAppAbout();
	DECLARE_MESSAGE_MAP()
	afx_msg void OnHowto();
	afx_msg void OnScore();
};


/*PlaneGame.cpp line67*/
CPlaneGameApp theApp;

随后从CView派生CPlaneGameView,并调用OnInitialUpdate()方法:

/*PlaneGameView.cpp line125*/
void CPlaneGameView::OnInitialUpdate()
{
	CView::OnInitialUpdate();
	// TODO: 在此添加专用代码和/或调用基类
	//初始化游戏
	InitGame();
}

可以看到,我们在这里调用了 InitGame() 函数。你会发现正是这个函数,创建了很多游戏所需的对象,并启动了游戏。

MFC程序是由事件驱动的。当一个事件产生,程序将调用相对应的事件处理函数;而没有事件产生时,它将不会做任何额外的事情。我们知道,飞机大战需要不断自动移动飞机和子弹,也就是说我们需要不断产生一个事件,使画面刷新、对象移动。所以我们需要启动一个定时器,不断产生一个 WM_TIMER 事件,并使它不断调用 OnTimer()函数。

/*PlaneGameView.cpp line795*/
//它本来在InitGame()中,需要实现其他功能而移动了位置
SetTimer(1, 30, NULL);

/*PlaneGameView.cpp line710*/
void CPlaneGameView::OnTimer(UINT_PTR nIDEvent)
{
	//刷新游戏帧画面: 在内存DC上绘图
	UpdateFrame(m_pMemDC);
	AI();

	CView::OnTimer(nIDEvent);
}

UpdateFrame() 用于刷新图像,而 AI() 用于响应键盘事件和处理各种乱七八糟的事情。整个程序在定时器的作用下不断重复调用这两个函数,使整个游戏运行起来,直到游戏结束,程序被退出。退出时会产生 WM_DESTROY 事件,并调用 OnDestry() 函数:

/*PlaneGameView.cpp line719*/
void CPlaneGameView::OnDestroy()
{
	CView::OnDestroy();
	this->StopGame();
	// TODO: 在此处添加消息处理程序代码
}

/*PlaneGameView.cpp line132*/
void CPlaneGameView::StopGame()
{
	delete m_pMe;
	delete m_pFriend;
	delete m_pMemDC;
	delete m_pDC;
	delete m_pMemBitmap;
}

InitGame() 函数中被创建的对象都将在 StopGame() 函数中被释放。这里需要注意的是, delete 一个空指针并不会产生错误。通读程序后可以发现, m_pMe 和 m_pFriend 大部分时候都被 delete 了两遍,因此在第一次 delete 时,我们需要将它置为 NULL ,否则会成为野指针并在第二次 delete 时产生段错误。

几点不同

由于我的修改,整个程序的运行和老师提供的模板程序有几点不同。

  • OnCreate() 函数

为了在游戏开始之前有一个界面,供玩家选择模式,我为 WM_CREATE 事件添加了事件处理函数。这主要是指导程序产生窗口用的,动态创建了按钮。代码比较长便不全粘贴:

/*PlaneGameView.cpp line726*/
int CPlaneGameView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{...}

通过消息映射(作用是将各个控件产生的事件和与之对应的消息处理函数绑定),映射到 OnButtonClick() 函数,对不同的模式初始化一些变量,并释放这些不再需要的按钮:

/*PlaneGameView.cpp line751*/
void CPlaneGameView::OnButtonClick(UINT uID)
{
	switch (uID) {
	case ...: {
		...
	}
	default:
		break;
	}

	//启动游戏
	SetTimer(1, 30, NULL);

	delete button1;
	delete button2;
	delete button3;
	delete button4;
	button1 = button2 = button3 = button4 = NULL;
}

容易发现,我是在按钮被按下后再启动游戏的。

  • pauseGame() 函数

我们能启动一个定时器,当然也能停止一个计时器。 KillTimer() 就是用来停止一个计时器。 SetTimer(1, 30, NULL); 中,第一个参数是定时器的标识,我们用 KillTimer(<标识>) 即可停止这个计时器:

/*PlaneGameView.cpp line804*/
void CPlaneGameView::pauseGame()
{
	KillTimer(1);
	CPauseDlg pauseDlg;
	pauseDlg.DoModal();
}

pauseDlg 是一个对话框,提示游戏已经暂停是否继续。这里需要注意的是, SetTimer() 并不是任何地方都可以调用的,它更像是 CPlaneGameView 的一个方法。所以我在某个合适的时候存储了 CView 对象的指针,通过指针来调用 SetTimer() 以实现游戏的继续。

本章完

by SDUST weilinfox

本文地址 https://www.cnblogs.com/weilinfox/p/13390862.html

前章 https://www.cnblogs.com/weilinfox/p/13229805.html

续章 https://www.cnblogs.com/weilinfox/p/13391352.html

posted @ 2020-07-28 14:23  八衛門狸  阅读(58)  评论(0编辑  收藏