1. 程式人生 > >C++學習 2018-12-11

C++學習 2018-12-11

1.飛機大戰

1.完成FoePlaneBox類

1.與子彈盒子類似,需要一個用來存放所有飛機的連結串列:list<CFoePlane*> m_lstFoePlane。
2.需要有三個成員函式:CreateFoePlane、AllFoePlaneShow、AllFoePlaneShow。

1)CreateFoePlane 函式用來建立敵人飛機,在建立敵人飛機的時候,我們需要產生不同型別飛機的頻率不同(意思是每產生一架大飛機能產生三架中飛機或六架小飛機),一個簡單的辦法是通過隨機數產生一個int值,然後按照不同的區間進行劃分;
2)建立飛機並初始化,注意:這裡建立的飛機是父型別 FoePlane 型的指標,指向子型別 new 出來的物件;
3)呼叫各自的InitFoePlane函式初始化,並將飛機加入到連結串列中;
4)顯示所有飛機,需要 AllFoePlaneShow 函式遍歷 m_lstFoePlane 連結串列來實現;
5)所有飛機進行移動,需要先判斷該機是否已經不在視窗內部顯示(即判斷y的值),若是則刪除該飛機,否則呼叫其自己的show函式;

6)主要程式碼:

#include "FoePlaneBox.h"


CFoePlaneBox::CFoePlaneBox(void)
{
}

CFoePlaneBox::~CFoePlaneBox(void)
{
	list<CFoePlane*>::iterator ite = m_lstFoePlane.begin();
	while (ite != m_lstFoePlane.end())
	{
		delete(*ite);
		ite = m_lstFoePlane.erase(ite);
	}
}

void CFoePlaneBox::AllFoePlaneMove
() { list<CFoePlane*>::iterator ite = m_lstFoePlane.begin(); while (ite != m_lstFoePlane.end()) { if((*ite)->y > 550) { delete(*ite); ite = m_lstFoePlane.erase(ite); } else { (*ite)->FoePlaneMove(); ite++; } } } void CFoePlaneBox::AllFoePlaneShow(HDC dc) { list<
CFoePlane*>::iterator ite = m_lstFoePlane.begin(); while (ite != m_lstFoePlane.end()) { (*ite)->FoePlaneShow(dc); ite++; } } void CFoePlaneBox::CreateFoePlane(HINSTANCE hIns) { CFoePlane *new_foe = 0; int flag = rand()%12; if(flag >=0 && flag <= 2) { new_foe = new CBigFoePlane; } if(flag >=3 && flag <= 6) { new_foe = new CMidFoePlane; } if(flag >=6 && flag <= 12) { new_foe = new CSmallFoePlane; } new_foe->InitFoePlane(hIns); m_lstFoePlane.push_back(new_foe); }
3.當我們完成上述步驟之後,我們就已經完成了敵人飛機的大部分程式碼,剩下的就是將敵人飛機進行呼叫,那麼該如何進行呼叫呢?

1)我們需要在 PlaneApp 的成員中加入 FoePlaneBox 類物件來記錄敵人飛機;
2)需要在 PlaneApp 的成員函式 OnCreateGame 中設定敵人飛機生成的定時器 FOE_PLANE_CREATE_TIMER_ID、敵人飛機移動的定時器 FOE_PLANE_MOVE_TIMER_ID;
3)需要在 PalneApp 的成員函式 OnGameRun 中設定當上述 FOE_PLANE_CREATE_TIMER_ID 定時器訊息來臨時,我們呼叫敵人飛機盒子的 CreateFoePlane 函式;
4)在 PalneApp 的成員函式 OnGameRun 中設定當上述 FOE_PLANE_MOVE_TIMER_ID 定時器訊息來臨時,我們呼叫敵人飛機盒子的 AllFoePlaneMove 函式;
5)完成上述程式碼之後就可以看到移動的敵人飛機了!!!

2.判斷敵人飛機是否被擊中

1.判斷敵人飛機是否被擊中就是通過子彈的x、y和敵人飛機的x、y以及寬高來比較得到是否擊中。
2.對於不同型別的敵人飛機,不同之處在於每個飛機的寬和高不同,其他基本相同。

1)對於大飛機:

bool CBigFoePlane::IsGunnerHitForPlane(const CGunner* pGunner)
{
	if(pGunner->x > this->x && pGunner->x < this->x+108
		&& pGunner->y > this->y && pGunner->y < this->y+128)
	{
		return TRUE;
	}
	return FALSE;
}

2)對於中等飛機:

bool CMidFoePlane::IsGunnerHitForPlane(const CGunner* pGunner)
{
	if(pGunner->x > this->x && pGunner->x < this->x+70
		&& pGunner->y > this->y && pGunner->y < this->y+90)
	{
		return TRUE;
	}
	return FALSE;
}

3)對於小型飛機:

bool CSmallFoePlane::IsGunnerHitForPlane(const CGunner* pGunner)
{
	if(pGunner->x > this->x && pGunner->x < this->x+32
		&& pGunner->y > this->y && pGunner->y < this->y+28)
	{
		return TRUE;
	}
	return FALSE;
}

3.設計一個儲存爆炸飛機的類

1.我們可以將所有的飛機分為兩類,一類是有血的飛機,另一類是沒血了的飛機,沒血了的飛機我們要播放(雖然很高大上,但實質上也是貼圖…)爆炸效果,那麼對於沒血了的飛機,我們也需要一個類似於 FoePlaneBox 的類來儲存。
2.將這個類命名為 BlastFoePlaneBox 類,該類有一個成員:list<CFoePlane*> m_lstBlastFoePlane,成員方法包括顯示飛機 AllBlastFoePlaneShow 和 改變顯示的飛機的ID ChangeShowID。

1)對於 ChangeShowID 函式,我們想要做的就是將 foeplane 的 showid 進行–,若減到零則刪除飛機;
2)對於 AllBlastFoePlaneShow 函式,遍歷 m_lstBlastFoePlane 連結串列,對於每個飛機呼叫其自身的 FoePlaneShow 函式即可;

3)主要程式碼:

#include "BlastFoePlaneBox.h"


CBlastFoePlaneBox::CBlastFoePlaneBox(void)
{
}

CBlastFoePlaneBox::~CBlastFoePlaneBox(void)
{
	list<CFoePlane*>::iterator ite = m_lstBlastFoePlane.begin();
	while (ite != m_lstBlastFoePlane.end())
	{
		delete(*ite);
		ite = m_lstBlastFoePlane.erase(ite);
	}
}

void CBlastFoePlaneBox::AllBlastFoePlaneShow(HDC dc)
{
	list<CFoePlane*>::iterator ite = m_lstBlastFoePlane.begin();
	while (ite != m_lstBlastFoePlane.end())
	{
		(*ite)->FoePlaneShow(dc);
		++ite;
	}
}

void CBlastFoePlaneBox::ChangeShowID()
{
	list<CFoePlane*>::iterator ite = m_lstBlastFoePlane.begin();
	while (ite != m_lstBlastFoePlane.end())
	{
		if((*ite)->m_nShowID == 0)
		{
			delete(*ite);
			ite = m_lstBlastFoePlane.erase(ite);
		}
		else
		{
			((*ite)->m_nShowID)--;
			ite++;
		}
	}
}

4.判斷子彈是否擊中飛機

1.對於子彈是否能擊中飛機,我們如何判斷?

1)由於我們將所有的子彈和所有的敵人飛機都放入了連結串列中,因此首先需要進行遍歷子彈連結串列;
2)在遍歷子彈連結串列的迴圈中,需要巢狀一個遍歷敵人飛機的迴圈(即每一個子彈都與所有飛機進行判斷,儘管這種方法很笨,但到目前為止我還不會其他好一點方法);
3)對於每一個敵人飛機,都呼叫它的 IsGunnerHitForPlane 函式,若是,則刪除子彈,並讓敵人飛機掉血,若敵人飛機沒血了(IsBoom 函式返回true),則將該飛機新增到新的 blast_foeplane_box 物件中去,並刪除這個敵人飛機;
4)在以上過程當中,我們會有一次刪除子彈的操作,當擊中飛機後,我們刪除該子彈,但是遍歷子彈連結串列仍會向後移動一個節點,這將會導致我們少遍歷了一個子彈,因此我們用一個 bool 型變數 flag_of_hit 來記錄子彈是否擊中飛機,若是,則置為 true,並不進行子彈連結串列自增的操作,若否,則讓子彈連結串列的迭代器自增;

5)主要程式碼:

void CPlaneApp::GunnerHitFoePlane()
{
	bool flag_of_hit = false;
	list<CGunner*>::iterator ite_gunner = gun_box.m_lstGunner.begin();
	// 遍歷所有子彈
	while (ite_gunner != gun_box.m_lstGunner.end())
	{
		// 每一個子彈是否擊中飛機
		list<CFoePlane*>::iterator ite_foe_plane = foe_plane_box.m_lstFoePlane.begin();
		while (ite_foe_plane != foe_plane_box.m_lstFoePlane.end())
		{
			if((*ite_foe_plane)->IsGunnerHitForPlane(*ite_gunner) == true)		// 當前子彈擊中了飛機
			{
				flag_of_hit = true;					// 擊中了就標記為true
				// 刪除炮彈
				delete(*ite_gunner);
				ite_gunner = gun_box.m_lstGunner.erase(ite_gunner);
				(*ite_foe_plane)->DownBlood();
				if((*ite_foe_plane)->IsBoom() == true)			// 如果飛機沒血了
				{
					blast_foeplane_box.m_lstBlastFoePlane.push_back(*ite_foe_plane);
					ite_foe_plane = foe_plane_box.m_lstFoePlane.erase(ite_foe_plane);
				}
				break;
			}
			++ite_foe_plane;
		}
		if(flag_of_hit)
			flag_of_hit = false;
		else
			++ite_gunner;
	}
}

5.顯示爆炸效果

1.在上面我們完成了 BlastFoePlane 類,可以通過設定定時器來顯示爆炸效果了。

1)在 PlaneApp 的 OnGameRun 函式中設定一個 CHANGE_PLANE_SHOWID_TIMER_ID 的定時器,當這個定時器訊息來到時,我們就呼叫 blast_foeplane_box.ChangeShowID();
2)增加一個小功能:按下空格鍵,所有的敵人飛機都爆炸(是不是外掛啊喂~);

3)主要程式碼:

void CPlaneApp::OnGameRun(WPARAM w_param)
{
	if(w_param == PLAYER_MOVE_TIMER_ID)
	{
		if(::GetAsyncKeyState(VK_LEFT))
			player.MovePlayer(VK_LEFT);
		if(::GetAsyncKeyState(VK_RIGHT))
			player.MovePlayer(VK_RIGHT);
		if(::GetAsyncKeyState(VK_DOWN))
			player.MovePlayer(VK_DOWN);
		if(::GetAsyncKeyState(VK_UP))
			player.MovePlayer(VK_UP);
		
	}

	if(w_param == BACK_MOVE_TIMER_ID)
	{
		back.MoveBack();
		
	}
	if(w_param == SEND_GUNNER_TIMER_ID)
	{
		player.SendGunner(gun_box, m_h_ins);
	}
	if (w_param == GUNNER_MOVE_TIMER_ID)		// 每次炮彈移動都需要進行判斷是否擊中了飛機
	{
		gun_box.AllGunnerMove();
		this->GunnerHitFoePlane();
	}
	// 敵方飛機生成
	if(w_param == FOE_PLANE_CREATE_TIMER_ID)
	{
		foe_plane_box.CreateFoePlane(m_h_ins);
	}
	// 敵方飛機移動
	if(w_param == FOE_PLANE_MOVE_TIMER_ID)
	{
		foe_plane_box.AllFoePlaneMove();
	}
	// 飛機爆炸顯示圖
	if(w_param == CHANGE_PLANE_SHOWID_TIMER_ID)
	{
		blast_foeplane_box.ChangeShowID();
	}
	this->OnGameShow();
}

void CPlaneApp::OnGameKeyDown(WPARAM w_param)		// 外掛函式
{
	// 按下空格鍵將所有敵人飛機銷燬
	if(w_param == VK_SPACE)
	{
		blast_foeplane_box.m_lstBlastFoePlane.splice(blast_foeplane_box.m_lstBlastFoePlane.end(), foe_plane_box.m_lstFoePlane);
	}
}

-------------------------------------------未完待續-------------------------------