MFC實現簡單飛機大戰(含遊戲聲音)

1 實驗內容

本實驗主要是實現簡單的飛機大戰遊戲,包含遊戲聲音、碰撞後爆炸效果,有大小敵機等。所用到的知識點如下:

1.貼圖技術

2.飛機類、子彈類實現

3.位圖移動

4.碰撞判斷,實現爆炸效果

5.插入聲音

此實驗的大概設計:遊戲畫面保持有一架大敵機,五架小敵機,一架戰機,30顆子彈。如果子彈擊落了敵機,那麼敵機對象不會被delete,而僅僅是改變它的位置,讓它重新從上面飛下,姑且可以叫做“假摧毀”吧。同理,子彈飛出界面或打到敵機,該子彈對象也不會被delete,而是從底部重新上升。擊落敵機時會有爆炸效果;戰機被敵機摧毀會有更大的爆炸效果,且遊戲會停1秒再繼續。

2 實驗環境

win10+vs2015

3 實驗步驟

3.1 添加遊戲資源

從網上下載自己喜歡的飛機和子彈圖片(.png文件),下載遊戲背景音樂(.wav文件),把上述資源導入本工程的res文件,複製res文件到Debug文件中,這樣便可以直接打開zsr_簡單飛機大戰.exe

打開res文件,可以看到下圖(當然啦,飛機、子彈和遊戲聲音選你喜歡的就行啦,不用跟我的一樣):


上圖中倒數第二個黑色的飛機就是我的程序圖標。

3.2 修改程序圖標

點擊[這裏]瞭解一下。

3.3 設置類

右鍵‘項目’,點擊‘添加類’,就可以添加類啦。本實驗設置4個類:大敵機(DADIJI),小敵機(XIAODIJI),戰機(ZHANJI),子彈(ZIDAN)。並且在類中實現貼圖。

3.3.1 大敵機類(DADIJI)

添加大敵機類,類名爲'DADIJI'。並在類中添加數據成員和成員函數,修改構造函數。具體如下:

//在類中加入:
CImage m_hero;//圖片類對象
	int diji_x;//圖片橫座標
	int diji_y;//圖片軸座標
	void JIDRAW(CDC *cDC) {  //設定大敵機的大小
		m_hero.Draw(*cDC, diji_x, diji_y, 100, 50);//圖片的寬爲100,高50
	}

//修改構造函數
DADIJI::DADIJI()
{
 //加載圖片
	CString imgPath = _T("res\\LXPlane.png");
	m_hero.Load(imgPath);
 //初始化大敵機的位置
	diji_x = 300;
	diji_y = 0;
}

3.3.2 小敵機類(XIAODIJI)

添加內容和大敵機類似,如下:

//類中加入:
	CImage m_hero;
	int diji_x;
	int diji_y;
	//設定小敵機的大小
void JIDRAW(CDC *cDC) {
		m_hero.Draw(*cDC, diji_x, diji_y, 50,50); //小敵機寬50,高50
	}
//修改構造函數:
 XIAODIJI::XIAODIJI()
{
 //加載圖片
	CString imgPath = _T("res\\BluePlane.png");
	m_hero.Load(imgPath);
 //設定小敵機的初始位置
	diji_x = 30;
	diji_y = 0;
}

3.3.3 戰機類(ZHANJI)

修改內容和大敵機差不多。這裏說明一下,我的戰機位置也用了diji_x和diji_y來表示,其實爲了代碼的可讀性,這裏用zhanji_x和zhanji_y比較好。(但我比較懶,這裏就不修改了。)具體改動的代碼如下:

//在類中加入:
CImage m_hero;
	int diji_x;
	int diji_y;
	CImage m_hero1;
//設定戰機的大小
	void JIDRAW(CDC *cDC) {
		m_hero.Draw(*cDC, diji_x, diji_y, 80, 80); 
	}
//加入爆炸效果函數
	void JIDRAW1(CDC *cDC, int x, int y) {
		m_hero1.Draw(*cDC, x, y, 100, 100);//戰機被敵機碰撞將變成爆炸狀
	}
//修改構造函數:
ZHANJI::ZHANJI()
{
//加載戰機圖片
	CString imgPath = _T("res\\XPlane.png");
	m_hero.Load(imgPath);
//加載爆炸圖片
	CString imgPath1 = _T("res\\ZHANJIBAOZHA.png");
	m_hero1.Load(imgPath1);
//設定戰機的初始位置
	diji_x = 330;
	diji_y = 500;
}

3.3.4 子彈類(ZIDAN)

這裏的解釋,和戰機類的解釋一樣。具體代碼如下:

//在類中加入:
	CImage m_hero;
	CImage m_hero1;
	int diji_x;
	int diji_y
//設置子彈的大小;
	void JIDRAW(CDC *cDC) {
		m_hero.Draw(*cDC, diji_x, diji_y, 20, 20); 
	}
  //設置爆炸效果
	void JIDRAW1(CDC *cDC,int x,int y) {
		m_hero1.Draw(*cDC, x, y, 50, 50);//子彈碰到敵機,將會變成爆炸狀
	}
修改構造函數:
ZIDAN::ZIDAN()
{
//加載子彈圖片
	CString imgPath = _T("res\\zidan.png");
	m_hero.Load(imgPath);
//加載爆炸圖片
	CString imgPath1 = _T("res\\baozha.png");
	m_hero1.Load(imgPath1);
設定子彈初始位置
	diji_x = 320;
	diji_y = 600;
}

3.4 遊戲重要變量

//在view類中加入下面變量:
	DADIJI  DA1;//大敵機1臺
	XIAODIJI   XIAO1[5];//保持遊戲界面有小敵機5臺
	ZIDAN ZI[30];//保持遊戲界面有子彈30顆
	ZHANJI ZHAN;//戰機一臺

3.5 設計遊戲開始界面

//修改view中的Oncreat()函數,如下:
int Czsr_簡單飛機大戰View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// TODO:  在此添加您專用的創建代碼
	PlaySound(_T("res\\youxi.wav"), NULL, SND_FILENAME | SND_ASYNC|SND_LOOP);//循環播放背景音樂
	this->SetTimer(1, 100, NULL);//設置一個定時器
	int x_num = 30;
	for (int j3 = 0;j3 < 5;j3++)
	{
		XIAO1[j3].diji_x += x_num;//開始時,小飛機的橫座標相隔30
		x_num += 30;
		XIAO1[j3].diji_y -= x_num;//開始時,小飛機的縱座標相隔30,起到延遲出現的效果
	}
 
	int num = 0;
	for (int j2 = 0;j2 < 30;j2++)
	{
		ZI[j2].diji_y-= num;//開始時,子彈的縱座標相隔25,起到延遲出現的效果
		ZI[j2].diji_x = ZHAN.diji_x+30;//子彈的橫座標在戰機的正中間
		num += 25;
		;
	}
 
	return 0;
}

3.6 位圖移動和碰撞

看到3.5的修改中已經設置了一個計時器,這裏響應Ontimer()函數,如下:

//設計ontimer()函數(如下):
void Czsr_簡單飛機大戰View::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息處理程序代碼和/或調用默認值

		CDC *cDC = this->GetDC();   //獲得當前窗口的DC     
		GetClientRect(&m_client);   //獲得窗口的尺寸  
		CRect rect;
		GetClientRect(rect);
		cDC->FillSolidRect(rect, RGB(255, 255, 255));
 
		ZHAN.JIDRAW(cDC);//畫戰機
		

		//子彈
		for (int j1 = 0;j1 < 30;j1++)
		{
			if (ZI[j1].diji_x < DA1.diji_x+100&& ZI[j1].diji_x >DA1.diji_x - 100&&ZI[j1].diji_y < DA1.diji_y+50&& ZI[j1].diji_y >DA1.diji_y - 50)//判斷子彈是否和大戰機碰撞
			{
				int xx = DA1.diji_x;
				int yy = DA1.diji_y;
				ZI[j1].JIDRAW1(cDC,xx,yy );//若兩者產生碰撞,則出現爆炸效果
				ZI[j1].diji_y = ZHAN.diji_y;
				ZI[j1].diji_x = ZHAN.diji_x + 30;//發生爆炸的子彈,重新回到戰機的子彈口
				DA1.diji_x = rand() % 400;  //大敵機從上面的隨機位置重新出現
				while (DA1.diji_x<20)
					DA1.diji_x = rand() % 400;
				DA1.diji_y = 0;
			}
			else if (ZI[j1].diji_y < 0)//如果子彈飛到最高處,重回戰機子彈口
			{
				ZI[j1].diji_y = ZHAN.diji_y;
				ZI[j1].diji_x = ZHAN.diji_x + 30;
			}
			else
			ZI[j1].diji_y = ZI[j1].diji_y-10;//否則,繼續上升
			for (int i1 = 0;i1 < 5;i1++)
			{
				if (ZI[j1].diji_x < XIAO1[i1].diji_x + 50 && ZI[j1].diji_x >XIAO1[i1].diji_x - 50 && ZI[j1].diji_y <XIAO1[i1].diji_y + 50 && ZI[j1].diji_y >XIAO1[i1].diji_y - 50)//判斷每臺小敵機是否和子彈碰撞
				{
					int xx1 = XIAO1[i1].diji_x;
					int yy1 = XIAO1[i1].diji_y;
					XIAO1[i1].diji_x = rand() % 400;//若碰撞,小敵機重新從上面的隨機位置降落
					while (XIAO1[i1].diji_x<20)
						XIAO1[i1].diji_x = rand() % 400;
					XIAO1[i1].diji_y = -30;
			
					ZI[j1].JIDRAW1(cDC, xx1, yy1);//爆炸狀態
					ZI[j1].diji_y = ZHAN.diji_y;
					ZI[j1].diji_x = ZHAN.diji_x + 30;//子彈重新回到戰機子彈口
				}
			}
			ZI[j1].JIDRAW(cDC);//畫子彈
		}

		//小敵機
		for (int j = 0;j < 5;j++)
		{
			if (ZHAN.diji_x < XIAO1[j].diji_x + 50 && ZHAN.diji_x >XIAO1[j].diji_x -50 && ZHAN.diji_y < XIAO1[j].diji_y + 50 && ZHAN.diji_y >XIAO1[j].diji_y - 50)//判斷戰機是否和小戰機碰撞
			{
				int xxx = ZHAN.diji_x;
				int yyy = ZHAN.diji_y;
				ZHAN.JIDRAW1(cDC, xxx, yyy);//若碰撞,發生戰機爆炸
				Sleep(500);//停止500ms
				XIAO1[j].diji_x = rand() % 400;
				while (DA1.diji_x<20)
					XIAO1[j].diji_x = rand() % 400;//小敵機重新隨機降落
				DA1.diji_y = 0;
				ZHAN.diji_x = 300;
				ZHAN.diji_y = 500;//戰機回到初始位置
			}
			else if (XIAO1[j].diji_y > 690)//若小敵機走出遊戲界面,則重新隨機降落
			{
				XIAO1[j].diji_x = rand() % 400;
				while(XIAO1[j].diji_x<20)
					XIAO1[j].diji_x = rand() % 400;
				XIAO1[j].diji_y = 0;
			}
			XIAO1[j].diji_y += 10;//小戰機下降
			XIAO1[j].JIDRAW(cDC);
		}

		//大敵機
		if (ZHAN.diji_x < DA1.diji_x + 100 && ZHAN.diji_x >DA1.diji_x - 100 && ZHAN.diji_y < DA1.diji_y + 80 && ZHAN.diji_y >DA1.diji_y - 80)//判斷戰機是否和大敵機發生碰撞
		{
			int xxx = ZHAN.diji_x;
			int yyy = ZHAN.diji_y;
			ZHAN.JIDRAW1(cDC, xxx, yyy);//若碰撞,戰機爆炸,大敵機重新隨機降落
			Sleep(500);//停止500ms
			DA1.diji_x = rand() % 400;
			while (DA1.diji_x<20)
				DA1.diji_x = rand() % 400;
			DA1.diji_y = 0;
			ZHAN.diji_x = 300;//戰機回到初始位置
			ZHAN.diji_y = 500;
		}
		if (DA1.diji_y < 650)//大敵機下降
			DA1.diji_y = DA1.diji_y + 10;
		else
			DA1.diji_y = 0;//若大飛機飛出遊戲界面 ,重新隨機降落
		DA1.JIDRAW(cDC); //畫大敵機
		ReleaseDC(cDC);
 
	CView::OnTimer(nIDEvent);
}

3.7 戰機移動

爲方便,這裏的戰機只能左右移動。下面響應一個鍵盤左右鍵的函數:

void Czsr_簡單飛機大戰View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 在此添加消息處理程序代碼和/或調用默認值
	if (nChar == 37)//向左移
	{
		if (ZHAN.diji_x > 0)
			ZHAN.diji_x -= 10;
	}

			else 	
			{
				if (nChar == 39)//向右移
				if (ZHAN.diji_x <450)
					ZHAN.diji_x += 10;
			}
 
	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

3.8 刪除狀態欄

在框架類的OnCreate()函數中刪除響應代碼即可。

3.9  刪除菜單欄,固定窗口大小

修改框架類的PreCreateWindow()函數:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if( !CFrameWnd::PreCreateWindow(cs) )
		return FALSE;
	// TODO: 在此處通過修改
	//  CREATESTRUCT cs 來修改窗口類或樣式
	
	//zsr,刪除菜單項
	cs.hMenu = NULL;
	//禁止改變窗口大小
	cs.dwExStyle &= ~WS_EX_CLIENTEDGE;

	cs.style &= ~WS_MAXIMIZEBOX; //禁止窗口最大化

	cs.style &= ~WS_MINIMIZEBOX; //禁止窗口最小化

	cs.style &= ~WS_THICKFRAME;//使窗口不能用鼠標改變大小
 
	return TRUE;
}

4 遊戲展示


            初始樣式                                                                    遊戲中

 

            小敵機爆炸                                                                 大敵機、戰機爆炸

5 總結

這個實驗太過簡單,飛機數量已經固定且沒有計分功能。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章