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);
}
}
-------------------------------------------未完待續-------------------------------