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 總結
這個實驗太過簡單,飛機數量已經固定且沒有計分功能。