iT邦幫忙

2021 iThome 鐵人賽

DAY 30
1
Software Development

三十天內用C++寫出一個小遊戲系列 第 30

Day 30 - 故事的最後不是句點,是開始

我成功了嗎

經過了這段特別長的暑假,我好像學會了蠻多的東西的!我想對於一個有嚴重拖延症的患者而言,我看起來好似成功了!(嗎?)

好啦其實有些東西跟原本計畫的不太一樣,原本我想要把去年程式設計的功課全部都寫過一遍,但是後來發現寫題目的速度太慢,只好改成把上課的影片全部看過一遍,並把他的例子都看一遍這樣。

這樣的缺點其實顯而易見,因為像是在最後我寫的遊戲裡面,想要製作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,但是後來發現他會這樣跑....

https://youtu.be/H_PZ9adqqfc

其實還蠻好笑的,但是真的不知道怎麼解決他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

上一篇
Day 29 - 這個遊戲製作人瘋了吧
系列文
三十天內用C++寫出一個小遊戲30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
juck30808
iT邦研究生 1 級 ‧ 2021-10-12 18:27:25

恭喜完賽 (拍手!!!

foodchain iT邦新手 3 級 ‧ 2021-10-12 20:28:20 檢舉

感謝你!!

我要留言

立即登入留言