flappybirdもどきを作ってみるよ!

flappybirdみたいな単純なゲームが好きです。消えてしまったのは残念ですが。
というわけでミニゲームとしてのいい例だと思うのでflappybirdもどきを作ります。

今回はC++&Siv3Dを使ってみます。Siv3Dを使うことでC++で手軽にサクッとミニゲームなどが作れるので、おすすめです。
あ、あと、SyntaxHighlighterというプラグインを入れて、以前のブログよりもソースコードが見やすく貼りやすくなっているので、ソースを貼り付ける際に使ってみてください。

まずはプレイヤーを表示します。

#include <Siv3D.hpp>

void Main()
{
    Circle player(100, Window::Height()/2, 15);
    Vec2 playerSpeed = { 0, 0 };
    const Vec2 g(0, 0.5);

    while (System::Update())
    {
        playerSpeed += g;//自由落下
        if (Input::KeySpace.clicked) playerSpeed = { 0, -8.0 };//スペースキーでジャンプ
        player.moveBy(playerSpeed);

        player.draw();
    }
}

Siv3Dには便利なベクトルクラスがあるので使っています。ベクトルクラスがC++11の初期化リストにも対応しているのでシンプルな書き方が出来ます。

次に障害物である壁を追加します。以下、変更部分をハイライトしてます。

#include <Siv3D.hpp>
#include <list>

void Main()
{
    Circle player(100, Window::Height()/2, 15);
    Vec2 playerSpeed = { 0, 0 };
    const Vec2 g(0, 0.5);
    std::list<RectF> walls;
    Point wallSize(50, Window::Height());
    double wallSpeed = 3.0;
    int cnt = 0;

    while (System::Update())
    {
        //ランダムな位置で壁を生成
        if (cnt % 100 == 0) {
            int r = Random(100, 300);
            walls.emplace_back(Window::Width(), r - Window::Height(), wallSize);
            walls.emplace_back(Window::Width(), r + 120, wallSize);
        }

        playerSpeed += g;//自由落下
        if (Input::KeySpace.clicked) playerSpeed = { 0, -8.0 };//スペースキーでジャンプ
        player.moveBy(playerSpeed);

        for (auto& wall : walls) {
            wall.moveBy(-wallSpeed, 0);
        }
        //必要のない壁を削除
        Erase_if(walls, [](RectF& r){ return r.pos.x < -50; });
        cnt++;

        player.draw();
        for (auto& wall : walls) wall.draw();
    }
}

連結リストを使って追加したり、必要なくなったら消したりしてます。C++11の範囲for文などを使うことでシンプルな書き方になっているのが最高に気持ちがいいですね。

壁を通過する毎にスコアを加算します。

#include <Siv3D.hpp>
#include <list>
#include <tuple>

void Main()
{
    Circle player(100, Window::Height()/2, 15);
    Vec2 playerSpeed = { 0, 0 };
    const Vec2 g(0, 0.5);
    std::list<std::tuple<RectF, bool>> walls;//通過フラグを追加
    Point wallSize(50, Window::Height());
    double wallSpeed = 3.0;
    int cnt = 0;
    int score = 0;
    Font font(30);

    while (System::Update())
    {
        //ランダムな位置で壁を生成
        if (cnt % 100 == 0) {
            int r = Random(100, 300);
            walls.emplace_back(RectF( Window::Width(), r - Window::Height(), wallSize ), false);
            walls.emplace_back(RectF( Window::Width(), r + 120, wallSize ), false);
        }

        playerSpeed += g;//自由落下
        if (Input::KeySpace.clicked) playerSpeed = { 0, -8.0 };//スペースキーでジャンプ
        player.moveBy(playerSpeed);

        int n = 0;
        for (auto& wall : walls) {
            std::get<0>(wall).moveBy(-wallSpeed, 0);
            //壁を初めて通過した場合
            if (std::get<0>(wall).x < player.x && !std::get<1>(wall)) {
                std::get<1>(wall) = true;//通過済みに
                n++;
                if (n == 2) score++;//壁を2個(上下)通過でスコア加算
            }
        }
        //必要のない壁を削除
        Erase_if(walls, [](std::tuple<RectF, bool> wall){ return std::get<0>(wall).x < -50; });
        cnt++;

        player.draw();
        for (auto& wall : walls) std::get<0>(wall).draw();
        font.drawCenter(Format(score), { Window::Width() / 2, 100 });
    }
}

tupleは少し違うかなーとか思いながら。多少汚くなっても気にしない心が完成させるには必要ですす…。

最後にゲームオーバー処理などを追加して終わりです。

#include <Siv3D.hpp>
#include <list>
#include <tuple>

void Main()
{
    Circle player(100, Window::Height()/2, 15);
    Vec2 playerSpeed = { 0, 0 };
    const Vec2 g(0, 0.5);
    std::list<std::tuple<RectF, bool>> walls;//通過フラグを追加
    Point wallSize(50, Window::Height());
    double wallSpeed = 3.0;
    int cnt = 0;
    int score = 0;
    Font font(30);
    bool gameOverFlag = false;

    while (System::Update())
    {
        if (gameOverFlag) {
            font.drawCenter(Format(L"SCORE: ",score), { Window::Width() / 2, Window::Height() / 2 });
            continue;
        }
        //ランダムな位置で壁を生成
        if (cnt % 100 == 0) {
            int r = Random(100, 300);
            walls.emplace_back(RectF( Window::Width(), r - Window::Height(), wallSize ), false);
            walls.emplace_back(RectF( Window::Width(), r + 120, wallSize ), false);
        }

        playerSpeed += g;//自由落下
        if (Input::KeySpace.clicked) playerSpeed = { 0, -8.0 };//スペースキーでジャンプ
        player.moveBy(playerSpeed);
        if (player.y > Window::Height()) gameOverFlag = true;

        int n = 0;
        for (auto& wall : walls) {
            std::get<0>(wall).moveBy(-wallSpeed, 0);
            //壁を初めて通過した場合
            if (std::get<0>(wall).x < player.x && !std::get<1>(wall)) {
                std::get<1>(wall) = true;
                n++;
                if (n == 2) score++;//壁を2個通過でスコア加算
            }
            //プレイヤーと壁の当たり判定
            if (std::get<0>(wall).intersects(player)) {
                gameOverFlag = true;
            }
        }
        //必要のない壁を削除
        Erase_if(walls, [](std::tuple<RectF, bool> wall){ return std::get<0>(wall).x < -50; });
        cnt++;

        player.draw();
        for (auto& wall : walls) std::get<0>(wall).draw(HSV(50 + std::get<0>(wall).x / 10));
        font.drawCenter(Format(score), { Window::Width() / 2, 100 });
    }
}

あと、このままでは一回しかプレイ出来ないので繰り返しプレイ出来るように初期化処理や、シーケンス処理を追加して上げる必要があると思います。

というわけでflappybirdぐらいのゲームなら簡単に作れるのでガシガシミニゲーム作りましょう!


コメントを残す

メールアドレスが公開されることはありません。