摘要:代碼下載點(diǎn)擊此處下載游戲截圖游戲編寫項(xiàng)目框架這個(gè)游戲主要的框架是這樣的其中有三個(gè)類派生自類,兩個(gè)派生自類。用于控制時(shí)間間隔。更新角色位置,并判斷是否與飛機(jī)或子彈碰撞。構(gòu)造函數(shù)初始化成員。
今天,我們來用所學(xué)知識(shí)做一個(gè)簡易的飛機(jī)大戰(zhàn)游戲。
玩家駕駛飛機(jī)在窗口下方左右移動(dòng),按下空格發(fā)射子彈(0.3秒一個(gè)),而上方會(huì)有石塊落下,打中飛機(jī)會(huì)死亡,玩家可以使用子彈攻擊石塊,如果打到了石塊就消失,同時(shí)之后的石塊下落會(huì)加速。屏幕上方還會(huì)有敵人的飛機(jī)出現(xiàn),會(huì)隨機(jī)發(fā)射子彈,還會(huì)隨機(jī)移動(dòng),玩家碰到敵人發(fā)來的子彈會(huì)死亡,敵人碰到玩家的子彈也會(huì)消失。敵人有20個(gè),隨機(jī)出現(xiàn),同一時(shí)刻屏幕上最多有5個(gè)敵人。玩家消滅所有的敵人就勝利了。
這個(gè)游戲主要的框架是這樣的:
其中有三個(gè)類派生自Actor類,兩個(gè)派生自DrawComponent類。
首先,用我們的項(xiàng)目模板創(chuàng)建一個(gè)項(xiàng)目。
注意:在所有出現(xiàn)顯示中文內(nèi)容的文件中都應(yīng)該加入一行#pragma execution_character_set("utf-8")
,否則是亂碼。
這個(gè)類是玩家控制的飛機(jī)類,功能主要有移動(dòng)和發(fā)射子彈。
Plane.h:
#pragma once#include"Actor.h"class Plane : public Actor{public: Plane(class Game* game, const Vector2& pos); virtual void ActorInput(const uint8_t* keyState); virtual void UpdateActor(float deltaTime);private: short mPlaneDir; Uint32 mTick;};
Plane.cpp:
#include "Plane.h"#include "Bullet.h"#include "DrawRectangleComponent.h"Plane::Plane(Game* game, const Vector2& pos) :Actor(game), mPlaneDir(0){ SetPosition(pos); mTick = SDL_GetTicks();}void Plane::ActorInput(const uint8_t* keyState){ mPlaneDir = 0; if (keyState[SDL_SCANCODE_RIGHT]) mPlaneDir += 1; if (keyState[SDL_SCANCODE_LEFT]) mPlaneDir -= 1; if (keyState[SDL_SCANCODE_SPACE] && SDL_TICKS_PASSED(SDL_GetTicks(), mTick + 300))//0.3秒發(fā)射一顆子彈 { mTick = SDL_GetTicks(); Vector2 pos = GetPosition(); pos.x += 20; pos.y -= 40; new DrawRectangleComponent(new Bullet(GetGame(), pos, -700), Vector2(10, 20), 255, 0, 0, 0); }}void Plane::UpdateActor(float deltaTime){ Vector2 pos = GetPosition(); pos.x += mPlaneDir * 300 * deltaTime; if (pos.x < 0) pos.x = 0; if (pos.x > 1024 - 50) pos.x = 1024 - 50; SetPosition(pos);}
mTick:上次發(fā)射子彈的時(shí)間。用于控制時(shí)間間隔。
mPlaneDir:飛機(jī)移動(dòng)方向。
初始化變量。
重寫的虛函數(shù)。首先設(shè)置移動(dòng)方向,然后判斷是否按下空格,如果按下就new一個(gè)子彈。
更新位置。
這個(gè)類是石頭類。
Stone.h:
#pragma once#include"Actor.h"class Stone :public Actor{public: Stone(Game* game, const Vector2& pos, float speed); virtual void UpdateActor(float deltaTime);private: float mSpeed;};
Stone.cpp:
#include "Stone.h"#include"Bullet.h"#include"Plane.h"#include #pragma execution_character_set("utf-8")Stone::Stone(Game* game, const Vector2& pos, float speed):Actor(game),mSpeed(speed){ SetPosition(pos);}void Stone::UpdateActor(float deltaTime){ Vector2 pos = GetPosition(); pos.y += deltaTime * mSpeed; if (pos.y > 768) SetState(EDead); SetPosition(pos); for (auto i : GetGame()->mActors) { if (typeid(*i) == typeid(Bullet))//運(yùn)行時(shí)類型檢查 { Vector2 bPos = i->GetPosition(); if (bPos.x + 20 > pos.x && bPos.x < pos.x + 50 && bPos.y < pos.y + 50) { SetState(EDead); i->SetState(EDead); GetGame()->mStoneSpeed *= 1.02; } } else if (typeid(*i) == typeid(Plane)) { Vector2 bPos = i->GetPosition(); if (bPos.x + 50 > pos.x && bPos.x < pos.x + 50 && bPos.y < pos.y + 50 && bPos.y + 30>pos.y) { SetState(EDead); i->SetState(EDead); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "游戲結(jié)束", "游戲結(jié)束,你輸了!", GetGame()->mWindow); GetGame()->mIsRunning = false; } } }}
mSpeed:速度。
初始化變量。
更新角色位置,并判斷是否與飛機(jī)或子彈碰撞。
其實(shí),這里的代碼我寫得非常非常非常非常非常非常非常非常非常非常(注:此處省略 1 0 1000000 10^{1000000} 101000000個(gè)非常)不規(guī)范。為什么呢?因?yàn)榘凑瘴覀兊拇a規(guī)范來說,這里應(yīng)該建立一個(gè)專門的碰撞檢測組件,并把它加到Actor里,如果簡單地寫在UpdateActor里面,會(huì)使代碼混亂,非常非常非常
(注:此處省略 1 0 1000000 10^{1000000} 101000000個(gè)非常)不便于閱讀和后續(xù)添加代碼。不過這里比較簡單 (其實(shí)是我偷懶) ,就將就看吧。
這個(gè)類是敵方飛機(jī)類,它可以自動(dòng)移動(dòng),并且隨機(jī)發(fā)射子彈。其實(shí)這里也寫得不太規(guī)范,其實(shí)我們大可不必建這個(gè)類,只需要建立Plane類,并不添加任何代碼,然后建兩個(gè)組件InputComponent和AutoMoveComponent,new對象的時(shí)候分別加上,這樣可以提高代碼復(fù)用率。在本例中,這個(gè)功能的好處并不明顯,但設(shè)想一下,如果飛機(jī)除了用戶控制和電腦控制之外,還有很多很多其它功能(比如為飛機(jī)添加彈夾和油量屬性),這樣專門寫兩個(gè)類就太麻煩了,不如使用組件。
Enemy.h:
#pragma once#include "Actor.h"class Enemy : public Actor{public: Enemy(Game* game, const Vector2& pos); virtual void UpdateActor(float deltatime);private: Uint32 mTicks; Uint32 mMoveTicks; short mMove;};
Enemy.cpp:
#include "Enemy.h"#include"Bullet.h"#include"DrawRectangleComponent.h"Enemy::Enemy(Game* game, const Vector2& pos) :Actor(game), mTicks(SDL_GetTicks()), mMoveTicks(SDL_GetTicks()){ SetPosition(pos); mMove = 200 + rand() % 100; if (rand() % 2) mMove = -mMove;}void Enemy::UpdateActor(float deltatime){ Vector2 pos = GetPosition(); if (SDL_TICKS_PASSED(SDL_GetTicks(), mMoveTicks + 1000))//隨機(jī)移動(dòng)位置 { mMoveTicks = SDL_GetTicks(); mMove = 100 + rand() % 100; if (rand() % 2) mMove = -mMove; } pos.x += deltatime * mMove; if (pos.x > 1024 - 50) pos.x = 1024 - 50; if (pos.x < 0) pos.x = 0; SetPosition(pos); if (SDL_TICKS_PASSED(SDL_GetTicks(), mTicks + 1000)&&!(rand()%25))//1秒發(fā)射子彈 { mTicks = SDL_GetTicks(); pos.x += 20; pos.y += 40; new DrawRectangleComponent(new Bullet(GetGame(), pos, 700), Vector2(10, 20), 255, 0, 0, 0); }}
mTicks:記錄上一次射擊的時(shí)間。
mMoveTicks:記錄以這個(gè)速度移動(dòng)的時(shí)間(因?yàn)橐S機(jī)移動(dòng),所以需要頻繁更新移動(dòng)速度和方向)。
mMove:移動(dòng)速度。
初始化成員。
先更新隨機(jī)移動(dòng)的速度,然后隨機(jī)移動(dòng)位置,最后發(fā)射子彈(1秒后,每幀有 1 25 /frac{1}{25} 251?幾率發(fā)射子彈)。
這個(gè)類是子彈類。
Bullet.h:
#pragma once#include "Actor.h"class Bullet : public Actor{public: Bullet(class Game* game, const Vector2& pos, float speed); virtual void UpdateActor(float deltaTime);private: float mSpeed;};
Bullet.cpp:
#include "Bullet.h"#include"Plane.h"#include"Enemy.h"#include #pragma execution_character_set("utf-8")Bullet::Bullet(Game* game, const Vector2& pos, float speed) :Actor(game), mSpeed(speed){ SetPosition(pos);}void Bullet::UpdateActor(float deltaTime){ Vector2 pos = GetPosition(); pos.y += mSpeed * deltaTime; SetPosition(pos); if (pos.y > 768 || pos.y < 0) SetState(EDead); for (auto i : GetGame()->mActors) { if (typeid(*i) == typeid(Enemy))//運(yùn)行時(shí)類型檢查 { Vector2 bPos = i->GetPosition(); if (bPos.x - 10 < pos.x && bPos.x + 50 > pos.x && bPos.y + 50 > pos.y) { SetState(EDead); i->SetState(EDead); } } else if (typeid(*i) == typeid(Plane)) { Vector2 bPos = i->GetPosition(); if (bPos.x - 10 < pos.x && bPos.x + 50 > pos.x && bPos.y < pos.y + 20 && bPos.y + 30 > pos.y) { SetState(EDead); i->SetState(EDead); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "游戲結(jié)束", "游戲結(jié)束,你輸了!", GetGame()->mWindow); GetGame()->mIsRunning = false; } } }}
Bullet類的代碼和Stone類的代碼基本相同,此處不再介紹。
最后,我們的任務(wù)是修改Game類。
首先,要在Game.cpp中包含所有自定義類的頭文件:
#include "Game.h"#include "SDL_image.h"#include #include "Actor.h"#include"Plane.h"#include"DrawPlaneComponent.h"#include"DrawRectangleComponent.h"#include"Stone.h"#include"Enemy.h"#include #include
接著,在Game.h中加入:
float mStoneSpeed;//石頭的速度 unsigned short mEnemyCount;//屏幕上敵人數(shù)量 unsigned short mAllEnemyCount;//剩余敵人數(shù)量
并在構(gòu)造函數(shù)里初始化這幾個(gè)變量。
然后,在LoadData函數(shù)里添加:
new DrawPlaneComponent(new Plane(this, Vector2(492, 700)));
new出飛機(jī)對象。
然后在UpdateGame里添加:
if (!(rand() % 100)) { new DrawRectangleComponent(new Stone(this, Vector2(rand() % (1024 - 50), 0), mStoneSpeed + rand() % 10), Vector2(50, 50), 255, 255, 0, 0); } if (mEnemyCount < 5 && mAllEnemyCount) { new DrawPlaneComponent(new Enemy(this, Vector2(rand() % 984, 10)), true); --mAllEnemyCount; ++mEnemyCount; }
用來new出敵人和石頭。
最后,在GenerateOutput函數(shù)里添加:
if (!mAllEnemyCount && !mEnemyCount) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "游戲結(jié)束", "游戲結(jié)束,你贏了!", mWindow); mIsRunning = false; }
用來提示勝利。注:最好添加在SDL_RenderPresent后面,這樣能顯示最后一個(gè)敵人消失的場景,要不然提示結(jié)束的時(shí)候屏幕上還有一個(gè)敵人。
到現(xiàn)在,整個(gè)的游戲就編寫完畢了,效果也就是開頭出示的那樣。通過這個(gè)項(xiàng)目,我們真正體會(huì)到了面向?qū)ο蟮暮锰帲约皩⒊绦蚰K化的重要性。最后,祝大家編程順利,代碼無bug!
注:博主馬上就要開學(xué)了,可能最近無法更新。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/121856.html
摘要:入門,第一個(gè)這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數(shù)式編程語言,它的代碼運(yùn)行在之上。它通過編輯類工具,帶來了先進(jìn)的編輯體驗(yàn),增強(qiáng)了語言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經(jīng)到來了,總結(jié)過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個(gè)這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數(shù)式編程語言,它的代碼運(yùn)行在之上。它通過編輯類工具,帶來了先進(jìn)的編輯體驗(yàn),增強(qiáng)了語言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經(jīng)到來了,總結(jié)過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個(gè)這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數(shù)式編程語言,它的代碼運(yùn)行在之上。它通過編輯類工具,帶來了先進(jìn)的編輯體驗(yàn),增強(qiáng)了語言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經(jīng)到來了,總結(jié)過去的 2017,相信小伙們一定有很多收獲...
摘要:本書主要圍繞一系列逐漸復(fù)雜的程序問題,以及用以解決這些問題的語言特性展開講解。你不只學(xué)到的函數(shù)和結(jié)構(gòu),也會(huì)學(xué)習(xí)到它們的設(shè)計(jì)目的和基本原理。因此我們把精力集中在最有價(jià)值的地方。本書不僅是對模板的權(quán)威解釋,而且本書還深入地介紹了其他一般的思想。 C++ 入門教程(41課時(shí)) - 阿里云大學(xué) C+...
摘要:我們目前正處于一個(gè)新興的區(qū)塊鏈開發(fā)行業(yè)中。,一種在以太坊開發(fā)人員中流行的新的簡單編程語言,因?yàn)樗怯糜陂_發(fā)以太坊智能合約的語言。它是全球至少萬開發(fā)人員使用的世界上最流行的編程語言之一。以太坊,主要是針對工程師使用進(jìn)行區(qū)塊鏈以太坊開發(fā)的詳解。 我們目前正處于一個(gè)新興的區(qū)塊鏈開發(fā)行業(yè)中。區(qū)塊鏈技術(shù)處于初期階段,然而這種顛覆性技術(shù)已經(jīng)成功地風(fēng)靡全球,并且最近經(jīng)歷了一場與眾不同的繁榮。由于許多...
閱讀 3759·2021-11-11 11:00
閱讀 2231·2021-10-08 10:05
閱讀 2897·2021-10-08 10:04
閱讀 3259·2021-09-30 09:48
閱讀 3933·2021-09-27 14:10
閱讀 1771·2021-09-09 09:33
閱讀 2163·2019-08-30 15:55
閱讀 1647·2019-08-30 13:53