經過了這段特別長的暑假,我好像學會了蠻多的東西的!我想對於一個有嚴重拖延症的患者而言,我看起來好似成功了!(嗎?)
好啦其實有些東西跟原本計畫的不太一樣,原本我想要把去年程式設計的功課全部都寫過一遍,但是後來發現寫題目的速度太慢,只好改成把上課的影片全部看過一遍,並把他的例子都看一遍這樣。
這樣的缺點其實顯而易見,因為像是在最後我寫的遊戲裡面,想要製作header file 還有他的 game.cpp的時候,忽然發現之前講過的東西快忘掉,不過也還好我都有打成文章,才可以讓我在打這些東西的時候有個依據可以參考。
是的,我做出來了! 雖然最終的成果跟我想像的完全不太一樣XD。
先來放上這次的影片內容吧!
他就是一個射擊遊戲,要利用 WSAD 控制上下左右,用滑鼠左鍵發射,然後 boss 也會射你,並且會一直追著你跑><。
開發的過程,其實沒有到很辛苦,因為大部分的內容都在之前的影片裏面有學到了,這次只有發現幾個比較困難的點。
因為飛船到滑鼠是一個變量,是一直隨著兩者的位置不同而改變,所以在這方面,就要使用小傑老師禁止"現在的我"使用的 vector。 對不起老師!
我在裡面設置了四個變量
2 for player
aimDir
,也就是玩家與滑鼠之間的向量aimDirNrom
,簡單說就是 A / |A| (A為向量)2 for boss
bAimDir
,也就是玩家與boss之間的向量bAimDirNrom
,簡單說就是 A / |A| (A為向量)用向量就可以讓砲彈從玩家射向指標,或是讓 boss 可以追蹤玩家。
原本我想要的砲彈是類似雷射光射出的感覺,但是後來發現,如果不設置 rotate,就只會指向同一個方向。為了解決這個問題,我用了 .rotate()
member funtion,但是後來發現他會這樣跑....
其實還蠻好笑的,但是真的不知道怎麼解決他QQ
未來有機會會找到方法的! 或是請各位大神們給個意見!
其他的部分就還好了!
但是比較特別的是,我把遊戲中自定義的 class 改放到 header file 中,整個版面變得比較清爽一些。
#include<SFML/Graphics.hpp>
#include<iostream>
using namespace std;
using namespace sf;
/*
遊戲所需要的 class 宣告
*/
class Bullet
{
public:
Sprite shape;
Vector2f currVelocity;
float maxSpeed;
Bullet(Texture* texture);
~Bullet() {}
};
class Shield
{
public:
Sprite shape;
Shield(Texture* texture, Vector2f pos);
~Shield(){}
};
class Player
{
public:
Sprite shape;
Texture* texture;
int lvl = 1;
int exp = 0;
int HP;
int HPMax;
int MP;
int MPMax;
// for bullets
vector<Bullet> bullets;
Vector2f currVelocity;
float maxSpeed;
// for shield
vector<Shield> shield;
Player(Texture* texture);
~Player() {}
};
class Boss
{
public:
Sprite shape;
int HP;
int HPMax;
vector<Bullet> bullets;
Vector2f currVelocity;
Boss(Texture* texture);
~Boss() {}
};
還有 Game.cpp 裡面
#include"MyGame.h"
#include<iostream>
#include<SFML/Graphics.hpp>
using namespace std;
using namespace sf;
Bullet::Bullet(Texture* texture) : currVelocity(0.f, 0.f), maxSpeed(10.f)
{
this->shape.setTexture(*texture);
this->shape.setScale(Vector2f(0.03f, 0.03f));
this->shape.rotate(90);
}
Shield::Shield(Texture* texture, Vector2f pos)
{
this->shape.setTexture(*texture);
this->shape.setScale(0.1f, 0.1f);
this->shape.setPosition(pos);
}
Player::Player(Texture* texture)
{
this->HPMax = this->lvl * 103 + 53;
this->HP = this->HPMax;
this->MPMax = this->lvl * 103 + 53;
this->MP = this->MPMax;
this->texture = texture;
this->shape.setTexture(*texture);
this->shape.setScale(Vector2f(0.15f, 0.15f));
}
Boss::Boss(Texture* texture)
{
this->HPMax = 500;
this->HP = this->HPMax;
this->shape.setTexture(*texture);
this->shape.setScale(0.5f, 0.5f);
}
最後就是遊戲的整段程式碼,裡面都有 comment 應該算寫得很仔細!
#include<iostream>
#include<SFML\Graphics.hpp>
#include<SFML\Window.hpp>
#include<SFML\System.hpp>
#include<sstream>
#include<cstdlib>
#include<math.h>
#include<cmath>
#include<vector>
#include"MyGame.h"
using namespace std;
using namespace sf;
int main()
{
srand(time(NULL));
RenderWindow window(VideoMode(1680, 840), "The Final Game!");
window.setFramerateLimit(200);
/*===========================================================//
Init Text
//===========================================================*/
Font font;
font.loadFromFile("font/Arial.ttf");
/*===========================================================//
Init Textures
//===========================================================*/
Texture playerTex;
playerTex.loadFromFile("textures/spaceship.png");
Texture humanBossTex;
humanBossTex.loadFromFile("textures/Boss1.png");
Texture earthBossTex;
earthBossTex.loadFromFile("textures/Boss2.png");
Texture deadHumanBossTex;
deadHumanBossTex.loadFromFile("textures/Boss1-2.png");
Texture deadEarthBossTex;
deadEarthBossTex.loadFromFile("textures/Boss2-3.png");
Texture bulletTex;
bulletTex.loadFromFile("textures/normalshoot.png");
Texture bossBulletTex;
bossBulletTex.loadFromFile("textures/enemyShoot.png");
/*===========================================================//
UI init
//===========================================================*/
Text gameOverText;
gameOverText.setFont(font);
gameOverText.setCharacterSize(120);
gameOverText.setFillColor(Color::Red);
gameOverText.setPosition(100.f, window.getSize().y / 2);
gameOverText.setString("Game Over!!!!");
Text youWin;
youWin.setFont(font);
youWin.setCharacterSize(120);
youWin.setFillColor(Color::Red);
youWin.setPosition(100.f, window.getSize().y / 2);
youWin.setString("You Win!!!!");
/*===========================================================//
Player init
//===========================================================*/
int score = 0;
Player player(&playerTex);
int shootTimer = 20;
// for hp 顯示
RectangleShape hpBar;
hpBar.setFillColor(Color::Red);
// for mp 顯示
RectangleShape mpBar;
mpBar.setFillColor(Color::Blue);
/*===========================================================//
Boss init
//===========================================================*/
Boss weirdFace(&humanBossTex);
weirdFace.shape.setPosition(window.getSize().x / 2, window.getSize().y / 2);
int bShootTimer = 15;
Text wHpText;
wHpText.setFont(font);
wHpText.setCharacterSize(12);
wHpText.setFillColor(Color::White);
RectangleShape bHpBar;
bHpBar.setFillColor(Color::Red);
/*===========================================================//
Bullet init
//===========================================================*/
Bullet b1(&bulletTex);
Bullet b2(&bossBulletTex);
vector<Bullet> pBullets;
vector<Bullet> bBullets;
Vector2f playerCenter;
Vector2f mousePosWindow;
Vector2f aimDir;
Vector2f aimDirNorm;
Vector2f rotateOrigin = Vector2f(1.f, 1.f);
Vector2f bossCenter;
Vector2f bAimDir;
Vector2f bAimDirNorm;
//cout << aimDirNorm.x << " " << aimDirNorm.y << endl;
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed)
window.close();
if (Keyboard::isKeyPressed(Keyboard::Escape))
window.close();
}
if (player.HP >= 0 && weirdFace.HP >= 0)
{
/*===========================================================//
【Update】 player
//===========================================================*/
// movement
if (Keyboard::isKeyPressed(Keyboard::W))
player.shape.move(0.f, -3.f);
if (Keyboard::isKeyPressed(Keyboard::S))
player.shape.move(0.f, 3.f);
if (Keyboard::isKeyPressed(Keyboard::D))
player.shape.move(3.f, 0.f);
if (Keyboard::isKeyPressed(Keyboard::A))
player.shape.move(-3.f, 0.f);
// collision with window
if (player.shape.getPosition().x <= 0) // left
player.shape.setPosition(0.f, player.shape.getPosition().y);
if (player.shape.getPosition().x >= window.getSize().x - player.shape.getGlobalBounds().width) // right
player.shape.setPosition(window.getSize().x - player.shape.getGlobalBounds().width, player.shape.getPosition().y);
if (player.shape.getPosition().y <= 0) // Top
player.shape.setPosition(player.shape.getPosition().x, 0.f);
if (player.shape.getPosition().y >= window.getSize().y - player.shape.getGlobalBounds().height) // bottom
player.shape.setPosition(player.shape.getPosition().x, window.getSize().y - player.shape.getGlobalBounds().height);
// hp & mp Bars and Text set
hpBar.setPosition(player.shape.getPosition().x + 10, player.shape.getPosition().y - 25.f);
mpBar.setPosition(player.shape.getPosition().x + 9, player.shape.getPosition().y - 15.f);
// HP & MP set
/*===========================================================//
Update】 controls
//===========================================================*/
if (shootTimer < 20)
shootTimer++;
/*===========================================================//
【Update】 Bullet
//===========================================================*/
playerCenter = Vector2f(player.shape.getPosition().x + 100.f, player.shape.getPosition().y - 15.f);
mousePosWindow = Vector2f(Mouse::getPosition(window));
aimDir = mousePosWindow - playerCenter;
aimDirNorm = Vector2f(aimDir.x / sqrt(pow(aimDir.x, 2) + pow(aimDir.y, 2)), aimDir.y / sqrt(pow(aimDir.x, 2) + pow(aimDir.y, 2)));
if (Mouse::isButtonPressed(Mouse::Left) && shootTimer >= 20)
{
b1.shape.setPosition(playerCenter);
b1.currVelocity = aimDirNorm;
pBullets.push_back(Bullet(b1));
shootTimer = 0; // reset timer
}
for (size_t i = 0; i < pBullets.size(); i++)
{
pBullets[i].shape.move(pBullets[i].currVelocity);
//Out of bounds
if (pBullets[i].shape.getPosition().x < 0 || pBullets[i].shape.getPosition().x > window.getSize().x
|| pBullets[i].shape.getPosition().y < 0 || pBullets[i].shape.getPosition().y > window.getSize().y)
{
pBullets.erase(pBullets.begin() + i);
}
else
{
//Enemy collision
if (pBullets[i].shape.getGlobalBounds().intersects(weirdFace.shape.getGlobalBounds()))
{
if (weirdFace.HP < 1)
{
weirdFace.shape.setTexture(deadHumanBossTex);
}
else
weirdFace.HP -= 5.3;
pBullets.erase(pBullets.begin() + i);
}
}
}
/*===========================================================//
【Update】 Enemy
//===========================================================*/
// Enemy bullets
Vector2f bossCenter = Vector2f(weirdFace.shape.getPosition().x + 100.f, weirdFace.shape.getPosition().y + 100.f);
bAimDir = playerCenter - bossCenter;
Vector2f bAimDirNorm = Vector2f(bAimDir.x / sqrt(pow(bAimDir.x, 2) + pow(bAimDir.y, 2)), bAimDir.y / sqrt(pow(bAimDir.x, 2) + pow(bAimDir.y, 2)));
if (bShootTimer >= 15)
{
b2.shape.setPosition(bossCenter);
b2.currVelocity = bAimDirNorm;
bBullets.push_back(Bullet(b2));
bShootTimer = 0; // reset timer
weirdFace.shape.move(bAirDirNorm * 10.f);
}
else
bShootTimer++;
for (size_t i = 0; i < bBullets.size(); i++)
{
bBullets[i].shape.move(bBullets[i].currVelocity);
//Out of bounds
if (bBullets[i].shape.getPosition().x < 0 || bBullets[i].shape.getPosition().x > window.getSize().x
|| bBullets[i].shape.getPosition().y < 0 || bBullets[i].shape.getPosition().y > window.getSize().y)
{
bBullets.erase(bBullets.begin() + i);
}
else
{
//Enemy collision
if (bBullets[i].shape.getGlobalBounds().intersects(player.shape.getGlobalBounds()))
{
player.HP -= 0.7;
bBullets.erase(bBullets.begin() + i);
}
}
}
// for enemy collision with player
if (weirdFace.shape.getGlobalBounds().intersects(player.shape.getGlobalBounds()))
{
player.HP -= 1.5;
player.shape.move(bAirDirNorm * 150.f);
}
bHpBar.setPosition(weirdFace.shape.getPosition().x + 5.f, weirdFace.shape.getPosition().y - 25.f);
/*===========================================================//
【Update】 UI
//===========================================================*/
hpBar.setSize(Vector2f((float)player.HP * 0.45f, 10.f));
mpBar.setSize(Vector2f((float)player.MP * 0.45f, 10.f));
bHpBar.setSize(Vector2f((float)weirdFace.HP * 0.45f, 10.f));
}
/*===========================================================//
【Draw】
//===========================================================*/
window.clear();
/*===========================================================//
【Draw】 player
//===========================================================*/
window.draw(player.shape);
window.draw(hpBar);
window.draw(mpBar);
/*===========================================================//
【Draw】 bullets
//===========================================================*/
for (size_t i = 0; i < pBullets.size(); i++)
{
window.draw(pBullets[i].shape);
}
for (size_t i = 0; i < bBullets.size(); i++)
{
window.draw(bBullets[i].shape);
}
/*===========================================================//
【Draw】 Boss
//===========================================================*/
window.draw(weirdFace.shape);
window.draw(bHpBar);
/*===========================================================//
【Draw】 UI
//===========================================================*/
if (player.HP <= 0)
{
window.draw(gameOverText);
}
if (weirdFace.HP <= 0)
{
window.draw(youWin);
}
window.draw(player.shape);
window.display();
}
}
最後真的成功做了出來!
其實我非常感動,因為我用到了一些我原本完全看不懂的東西,真的是從零開始慢慢學會的。
真的非常感謝 Sen 還有 Alu 找我參加這次的活動,沒有他們就沒有現在的我! 也謝謝他們在這幾個月的幫助與解惑(還有杰哥的解惑!) ! 大大大感謝 !
在這段時間,我很明顯發現到自己的成長,原本甚麼東西都看不懂,但是忍住一看到不懂就馬上想要問人的衝動,自己去 google,最後找到自己喜歡的答案,其實這段過程也會深深刻在腦海裏面,有時候也會在寫其他東西的時候突然想起來,真的是受益良多 !
雖然跟別人相比,我現在頂多只是 新手 + lvl.1(大概就是剛出新手村,要去弓箭手村解任務吧)(包含寫文章還有寫 code,還有表達能力QQ),但是我相信繼續紮實的訓練,也會成為一個厲害的人的!
希望看到這篇文章的人也可以成為你們想成為的人ㄉ,共勉之。
希望我未來看到我現在寫的東西會哭出來,這就代表我變強很多了><
還記得我們是個 team 吧,其他人寫的文章也都很強 ! 這邊推爆 !!!!!
【團隊成員】
Name | 標題 | Tags |
---|---|---|
ALu | NFT 網站與 MetaMask 連動的 Owners 登入系統 | BlockChain,Modern Web |
Cooksuhr | 1995到2021,php到react網站開發歷程 | Modern Web |
ExcitedMail | 杰哥的考研紀錄 | Computer Science |
foodchain | 三十天內用C++寫出一個小遊戲 | Software Development |
momojn | C++ 三十天學習紀錄 | Software Development |
chen_yanlong | 學密碼學也猜不到你的手機密碼 | Cryptography |
yywoli | 從資料庫到資料分析視覺化 | Data Analysis |