C++學習 2018-12-10
上回書我們說到將飛機和背景都貼到了視窗上,子彈也貼成功了,那麼現在就需要讓子彈能夠自主移動了。
1.飛機大戰
1.在上一次的飛機大戰設計中,我們發現了一個問題:飛機移動會一卡一卡的,這是因為我們在不同的重新整理週期進行貼背景圖和貼飛機圖導致的,需要一個有效的方法進行解決,這裡我們引入 雙緩衝 的概念。
1) 雙緩衝 是指我們在將某些東西(如五本書)從A處移動到B處時,正常的做法是一本一本移動,而為了省時省力,我們找一輛小車,將所有的書都裝入小車,我們將小車從A移到B也能夠完成任務;
2)這裡我們將所有的圖片都先貼到一箇中間dc上,再將該dc拷貝到主dc上。
1.實現雙緩衝
1)首先在GameApp中定義一箇中間點陣圖控制代碼 HBITMAP di_san_fang_tu,以及一箇中間dc控制代碼 HDC di_san_fang_dc;
2)在 PlaneApp 中的 OnCreateGame 函式中獲取主視窗控制代碼的dc,根據該dc建立 di_san_fang_tu 以及 di_san_fang_dc;
3)在 PlaneApp 中的 OnGameShow 函式中將 di_san_fang_tu 作為中間dc來將所有的圖片都貼到其上,再將 di_san_fang_tu 拷貝到dc上完成雙緩衝。
4)程式如下:
void CPlaneApp::OnCreateGame ()
{
dc = GetDC(m_hwnd);
di_san_fang_dc = ::CreateCompatibleDC(dc);
di_san_fang_tu = ::CreateCompatibleBitmap(dc,380, 550);
// 1.初始化背景
back.InitBack(m_h_ins);
// 2.初始化玩家飛機
player.InitPlayer(m_h_ins);
// 3.啟動所有的定時器
::SetTimer(m_hwnd, BACK_MOVE_TIMER_ID, 100, 0);
::SetTimer(m_hwnd, PLAYER_MOVE_TIMER_ID, 1, 0);
}
void CPlaneApp::OnGameShow()
{
::SelectObject(di_san_fang_dc, di_san_fang_tu);
back.ShowBack(di_san_fang_dc);
player.ShowPlayer(di_san_fang_dc);
gun_box.AllGunnerShow(di_san_fang_dc);
foe_plane_box.AllFoePlaneShow(di_san_fang_dc);
::BitBlt(dc, 0, 0, 380, 550, di_san_fang_dc, 0, 0, SRCCOPY);
}
2.繼續完成Gunner類
1.在設計Gunner類的初始化函式 InitGuuner 時,我們多給這個函式傳入兩個int型的變數,分別表示新的x值和新的y值。
2.MoveGunner 函式,由於子彈移動時只有y軸上的變化,因此只需要改變y值。
3.ShowGunner 函式完成貼圖。
4.主要程式碼
#include "Gunner.h"
CGunner::CGunner(void)
{
m_hBmpGunner = 0;
x = 0;
y = 0;
}
CGunner::~CGunner(void)
{
::DeleteObject(m_hBmpGunner);
}
void CGunner::InitGuuner(HINSTANCE hIns, int new_x, int new_y)
{
m_hBmpGunner = ::LoadBitmap(hIns, MAKEINTRESOURCE(IDB_GUNNER));
x = new_x;
y = new_y;
}
void CGunner::MoveGunner()
{
y -= 10;
}
void CGunner::ShowGunner(HDC dc)
{
HDC map_dc = ::CreateCompatibleDC(dc);
::SelectObject(map_dc, m_hBmpGunner);
::BitBlt(dc, x, y, 6, 9, map_dc, 0, 0, SRCCOPY);
::DeleteDC(map_dc);
}
3.子彈盒子類GunnerBox
##### 1.我們考慮一個問題:子彈是每一個間隔發射一次,那麼我們對每一個子彈進行單獨的操作的複雜度是最大的,因此我們將發射出去的子彈統一放入一個連結串列中,該連結串列作為一個類的成員來儲存。
2.既然GunnerBox類是儲存所有子彈的,那麼它需要能夠同時將這些子彈進行移動(通過遍歷連結串列),並且將這些子彈都顯示出來,因此該類的成員函式有:AllGunnerMove 以及 AllGunnerShow。
3.主要程式碼:
#include "GunnerBox.h"
CGunnerBox::CGunnerBox(void)
{
}
CGunnerBox::~CGunnerBox(void)
{
list<CGunner*>::iterator ite = m_lstGunner.begin(); // m_lstGunner是儲存所有子彈的連結串列,宣告為:list<CGunner*> m_lstGunner
while (ite != m_lstGunner.end())
{
delete(*ite);
ite = m_lstGunner.erase(ite);
}
}
void CGunnerBox::AllGunnerMove()
{
list<CGunner*>::iterator ite = m_lstGunner.begin();
while (ite != m_lstGunner.end())
{
if((*ite)->y < 0) // 當子彈的y<0,此時代表著子彈到達了視窗外,在視窗中無法看到,因此將其刪掉
{
delete(*ite);
ite = m_lstGunner.erase(ite);
}
else // 否則呼叫子彈自己的移動函式 MoveGunner
{
(*ite)->MoveGunner();
ite++;
}
}
}
void CGunnerBox::AllGunnerShow(HDC dc)
{
list<CGunner*>::iterator ite = m_lstGunner.begin();
while (ite != m_lstGunner.end())
{
(*ite)->ShowGunner(dc);
ite++;
}
}
4.當建立完子彈盒子之後,我們需要生成子彈,而不能再像之前那樣在 PlaneApp 中定義物件來顯示子彈了,因為那樣一來需要定義許多許多的子彈物件
1.我們定義子彈物件是在 Player 中的 SendGunner 函式裡實現的。
void CPlayer::SendGunner(CGunnerBox& gunnerBox, HINSTANCE hIns)
{
CGunner *new_gunner = new CGunner; // new一個子彈物件
new_gunner->InitGuuner(hIns, x+26, y-7); // 初始化子彈物件時將飛機的x與y傳入找到飛機頭的位置作為子彈的位置
gunnerBox.m_lstGunner.push_back(new_gunner); // 新增該子彈到連結串列
}
5.完成敵人飛機的介面類
1.因為敵人飛機有三種,一類小飛機,一類中飛機,一類大飛機,雖然有三個型別的敵人飛機,但是這三個敵人飛機的成員、成員函式都一樣,只是具體的型別有具體的方法,因此我們需要設計一個介面類來連結這些敵人飛機。
2.設計敵人飛機的介面類FoePlane
1)首先思考,敵人飛機會和子彈、玩家飛機發生碰撞,因此需要匯入子彈和玩家飛機的標頭檔案;
2)對於敵人飛機,有規定的血量來區分三種類型的飛機;
3)當敵人飛機爆炸時,我們需要改變顯示的飛機圖片的位置;
4)可以確定該類有以下成員變數:
#include "CommonInclude.h"
#include "Gunner.h"
#include "Player.h"
class CFoePlane
{
public:
HBITMAP m_hBmpFoePlane;
int m_nBlood;
int m_nShowID;
int x;
int y;
public:
CFoePlane(void);
virtual ~CFoePlane(void);
}
3.設計FoePlane類的成員函式
1)首先是與背景類、player類等類似的函式:InitFoePlane、FoePlaneMove、FoePlaneShow函式;
2)其次,敵人飛機是否爆炸,需要進行判斷,因此設計一個內斂布林函式:inline bool IsBoom;
3)然後,敵人飛機是否被子彈打中,設計函式:virtual bool IsGunnerHitForPlane;
4)敵人飛機若被打中,則需掉血,設計函式:inline void DownBlood;
5)判斷敵人飛機是否和玩家飛機發生了碰撞,設計函式:virtual bool IsHitPlayerPlane;
6)主要程式碼:
#pragma once
#include "CommonInclude.h"
#include "Gunner.h"
#include "Player.h"
class CFoePlane
{
public:
HBITMAP m_hBmpFoePlane;
int m_nBlood;
int m_nShowID;
int x;
int y;
public:
CFoePlane(void);
virtual ~CFoePlane(void);
public:
inline bool IsBoom()
{
if(m_nBlood == 0)
{
return true;
}
return false;
}
virtual bool IsGunnerHitForPlane(const CGunner* pGunner)=0;
virtual bool IsHitPlayerPlane(const CPlayer& plane)=0;
virtual void FoePlaneMove()=0;
virtual void FoePlaneShow(HDC dc)=0;
virtual void InitFoePlane(HINSTANCE hIns)=0;
inline void DownBlood()
{
m_nBlood--;
}
};
4.設計大型飛機 BigFoePlane 類
1)由於所需成員都在介面函式 FoePlane 中,因此只須完成六個虛擬函式(外加一個虛析構);
2)初始化飛機InitFoePlane,我們需要載入飛機點陣圖,給定該型別飛機的血量以及要顯示的點陣圖的編號(其實每一種敵人飛機點陣圖都是一張含有豎著的幾個飛機的點陣圖,那麼我們需要對這些飛機進行編號,來顯示爆炸時的飛機和沒爆炸的飛機);
3)我們需要在隨機位置產生飛機,因此將飛機的x設定為一個在視窗寬度以內的隨機數(這個“視窗寬度”是指減去敵人飛機寬度後的視窗寬度,因為飛機不可能顯示為半個飛機);
4)FoePlaneShow 函式用來顯示飛機;
5)FoePlaneMove 函式用來控制敵人飛機向下移動的距離;
6)其餘兩個虛擬函式暫時不做處理;
6)主要程式碼:
#include "BigFoePlane.h"
CBigFoePlane::CBigFoePlane(void)
{
}
CBigFoePlane::~CBigFoePlane(void)
{
::DeleteObject(m_hBmpFoePlane);
m_hBmpFoePlane = 0;
}
bool CBigFoePlane::IsGunnerHitForPlane(const CGunner* pGunner)
{
return false;
}
bool CBigFoePlane::IsHitPlayerPlane(const CPlayer& plane)
{
return false;
}
void CBigFoePlane::FoePlaneMove()
{
y += 2;
}
void CBigFoePlane::FoePlaneShow(HDC dc)
{
HDC map_dc = ::CreateCompatibleDC(dc);
::SelectObject(map_dc, m_hBmpFoePlane);
::BitBlt(dc, x, y, 108, 128, map_dc, 0, (3 - m_nShowID)*128, SRCAND);
::DeleteDC(map_dc);
}
void CBigFoePlane::InitFoePlane(HINSTANCE hIns)
{
m_hBmpFoePlane = ::LoadBitmap(hIns, MAKEINTRESOURCE(IDB_BIG));
m_nBlood = 5;
m_nShowID = 3;
x = rand()%(380-108);
y = -128;
}
5.其餘兩種飛機與該飛機基本完全一致,只是在飛機的大小上略有不同。
其餘兩種飛機的實現檔案:
1)中型飛機:
#include "MidFoePlane.h"
CMidFoePlane::CMidFoePlane(void)
{
}
CMidFoePlane::~CMidFoePlane(void)
{
::DeleteObject(m_hBmpFoePlane);
m_hBmpFoePlane = 0;
}
bool CMidFoePlane::IsGunnerHitForPlane(const CGunner* pGunner)
{
return FALSE;
}
bool CMidFoePlane::IsHitPlayerPlane(const CPlayer& plane)
{
return false;
}
void CMidFoePlane::FoePlaneMove()
{
y += 5;
}
void CMidFoePlane::FoePlaneShow(HDC dc)
{
HDC map_dc = ::CreateCompatibleDC(dc);
::SelectObject(map_dc, m_hBmpFoePlane);
::BitBlt(dc, x, y, 70, 90, map_dc, 0, (2 - m_nShowID)*90, SRCAND);
::DeleteDC(map_dc);
}
void CMidFoePlane::InitFoePlane(HINSTANCE hIns)
{
m_hBmpFoePlane = ::LoadBitmap(hIns, MAKEINTRESOURCE(IDB_MID));
m_nBlood = 3;
m_nShowID = 2;
x = rand()%(380-70);
y = -90;
}
2)小型飛機
#include "SmallFoePlane.h"
CSmallFoePlane::CSmallFoePlane(void)
{
}
CSmallFoePlane::~CSmallFoePlane(void)
{
::DeleteObject(m_hBmpFoePlane);
m_hBmpFoePlane = 0;
}
bool CSmallFoePlane::IsGunnerHitForPlane(const CGunner* pGunner)
{
return FALSE;
}
bool CSmallFoePlane::IsHitPlayerPlane(const CPlayer& plane)
{
return false;
}
void CSmallFoePlane::FoePlaneMove()
{
y += 10;
}
void CSmallFoePlane::FoePlaneShow(HDC dc)
{
HDC map_dc = ::CreateCompatibleDC(dc);
::SelectObject(map_dc, m_hBmpFoePlane);
::BitBlt(dc, x, y, 34, 28, map_dc, 0, (1 - m_nShowID)*28, SRCAND);
::DeleteDC(map_dc);
}
void CSmallFoePlane::InitFoePlane(HINSTANCE hIns)
{
m_hBmpFoePlane = ::LoadBitmap(hIns, MAKEINTRESOURCE(IDB_LITTLE));
m_nBlood = 1;
m_nShowID = 1;
x = rand()%(380-34);
y = -28;
}
------------------------------------------未完待續----------------------------------