摘要:代碼下載點擊此處下載游戲截圖游戲編寫項目框架這個游戲主要的框架是這樣的其中有三個類派生自類,兩個派生自類。用于控制時間間隔。更新角色位置,并判斷是否與飛機或子彈碰撞。構造函數初始化成員。
今天,我們來用所學知識做一個簡易的飛機大戰游戲。
玩家駕駛飛機在窗口下方左右移動,按下空格發射子彈(0.3秒一個),而上方會有石塊落下,打中飛機會死亡,玩家可以使用子彈攻擊石塊,如果打到了石塊就消失,同時之后的石塊下落會加速。屏幕上方還會有敵人的飛機出現,會隨機發射子彈,還會隨機移動,玩家碰到敵人發來的子彈會死亡,敵人碰到玩家的子彈也會消失。敵人有20個,隨機出現,同一時刻屏幕上最多有5個敵人。玩家消滅所有的敵人就勝利了。
這個游戲主要的框架是這樣的:
其中有三個類派生自Actor類,兩個派生自DrawComponent類。
首先,用我們的項目模板創建一個項目。
注意:在所有出現顯示中文內容的文件中都應該加入一行#pragma execution_character_set("utf-8")
,否則是亂碼。
這個類是玩家控制的飛機類,功能主要有移動和發射子彈。
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秒發射一顆子彈 { 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:上次發射子彈的時間。用于控制時間間隔。
mPlaneDir:飛機移動方向。
初始化變量。
重寫的虛函數。首先設置移動方向,然后判斷是否按下空格,如果按下就new一個子彈。
更新位置。
這個類是石頭類。
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))//運行時類型檢查 { 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, "游戲結束", "游戲結束,你輸了!", GetGame()->mWindow); GetGame()->mIsRunning = false; } } }}
mSpeed:速度。
初始化變量。
更新角色位置,并判斷是否與飛機或子彈碰撞。
其實,這里的代碼我寫得非常非常非常非常非常非常非常非常非常非常(注:此處省略 1 0 1000000 10^{1000000} 101000000個非常)不規范。為什么呢?因為按照我們的代碼規范來說,這里應該建立一個專門的碰撞檢測組件,并把它加到Actor里,如果簡單地寫在UpdateActor里面,會使代碼混亂,非常非常非常
(注:此處省略 1 0 1000000 10^{1000000} 101000000個非常)不便于閱讀和后續添加代碼。不過這里比較簡單 (其實是我偷懶) ,就將就看吧。
這個類是敵方飛機類,它可以自動移動,并且隨機發射子彈。其實這里也寫得不太規范,其實我們大可不必建這個類,只需要建立Plane類,并不添加任何代碼,然后建兩個組件InputComponent和AutoMoveComponent,new對象的時候分別加上,這樣可以提高代碼復用率。在本例中,這個功能的好處并不明顯,但設想一下,如果飛機除了用戶控制和電腦控制之外,還有很多很多其它功能(比如為飛機添加彈夾和油量屬性),這樣專門寫兩個類就太麻煩了,不如使用組件。
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))//隨機移動位置 { 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秒發射子彈 { mTicks = SDL_GetTicks(); pos.x += 20; pos.y += 40; new DrawRectangleComponent(new Bullet(GetGame(), pos, 700), Vector2(10, 20), 255, 0, 0, 0); }}
mTicks:記錄上一次射擊的時間。
mMoveTicks:記錄以這個速度移動的時間(因為要隨機移動,所以需要頻繁更新移動速度和方向)。
mMove:移動速度。
初始化成員。
先更新隨機移動的速度,然后隨機移動位置,最后發射子彈(1秒后,每幀有 1 25 /frac{1}{25} 251?幾率發射子彈)。
這個類是子彈類。
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))//運行時類型檢查 { 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, "游戲結束", "游戲結束,你輸了!", GetGame()->mWindow); GetGame()->mIsRunning = false; } } }}
Bullet類的代碼和Stone類的代碼基本相同,此處不再介紹。
最后,我們的任務是修改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;//屏幕上敵人數量 unsigned short mAllEnemyCount;//剩余敵人數量
并在構造函數里初始化這幾個變量。
然后,在LoadData函數里添加:
new DrawPlaneComponent(new Plane(this, Vector2(492, 700)));
new出飛機對象。
然后在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函數里添加:
if (!mAllEnemyCount && !mEnemyCount) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "游戲結束", "游戲結束,你贏了!", mWindow); mIsRunning = false; }
用來提示勝利。注:最好添加在SDL_RenderPresent后面,這樣能顯示最后一個敵人消失的場景,要不然提示結束的時候屏幕上還有一個敵人。
到現在,整個的游戲就編寫完畢了,效果也就是開頭出示的那樣。通過這個項目,我們真正體會到了面向對象的好處,以及將程序模塊化的重要性。最后,祝大家編程順利,代碼無bug!
注:博主馬上就要開學了,可能最近無法更新。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/121856.html
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
摘要:我們目前正處于一個新興的區塊鏈開發行業中。,一種在以太坊開發人員中流行的新的簡單編程語言,因為它是用于開發以太坊智能合約的語言。它是全球至少萬開發人員使用的世界上最流行的編程語言之一。以太坊,主要是針對工程師使用進行區塊鏈以太坊開發的詳解。 我們目前正處于一個新興的區塊鏈開發行業中。區塊鏈技術處于初期階段,然而這種顛覆性技術已經成功地風靡全球,并且最近經歷了一場與眾不同的繁榮。由于許多...
閱讀 3701·2021-11-11 11:00
閱讀 2180·2021-10-08 10:05
閱讀 2671·2021-10-08 10:04
閱讀 3204·2021-09-30 09:48
閱讀 3763·2021-09-27 14:10
閱讀 1704·2021-09-09 09:33
閱讀 2100·2019-08-30 15:55
閱讀 1601·2019-08-30 13:53