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