1. 程式人生 > 實用技巧 >SDUST 小學期飛機大戰講解 - 1框架

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() 函式,對不同的模式初始化一些變數,並釋放這些不再需要的按鈕:

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

續章 未編輯