Skip to content

トレーニング | 円を配置する

クリックで画面上に円を配置していくプログラムを作ります。

1. 基本のプログラム

左クリックされるたびに、Array<Circle>Circle 型の要素を追加していきます。
Array<Circle> の各要素には範囲 for でアクセスします。

# include <Siv3D.hpp>

void Main()
{
    // 円の半径
    constexpr double circleR = 24.0;

    // 円の色
    constexpr ColorF circleColor(0.4, 0.8, 0.6);

    // 円を格納する配列
    Array<Circle> circles;

    while (System::Update())
    {
        // もし左クリックされたら
        if (MouseL.down())
        {
            // 配列に円を追加
            circles << Circle(Cursor::Pos(), circleR);
        }

        // 配列内の円を描画
        for (const auto& circle : circles)
        {
            circle.draw(circleColor);
        }
    }
}

2. 容量を節約

円の半径が定数の場合、Circle を格納する代わりに円の中心座標である Vec2 を格納するだけでも大丈夫です。これで配列のメモリ消費量が 3 分の 2 になります (1 要素あたり 24 バイト → 16 バイト)。

# include <Siv3D.hpp>

void Main()
{
    // 円の半径
    constexpr double circleR = 24.0;

    // 円の色
    constexpr ColorF circleColor(0.4, 0.8, 0.6);

    // 円の中心座標を格納する配列
    Array<Vec2> circles;

    while (System::Update())
    {
        // もし左クリックされたら
        if (MouseL.down())
        {
            // 配列に円の中心座標を追加
            circles << Cursor::Pos();
        }

        // 配列内の円の中心座標を使って円を描画
        for (const auto& circle : circles)
        {
            Circle(circle, circleR).draw(circleColor);
        }
    }
}

3. シーン上のクリックに限る

ここまでのプログラムでは、ウィンドウを移動させようと、ウィンドウのタイトルバーをクリックしたときにも円が追加されてしまいます。
これを避けたい場合、条件を if (Scene::Rect().leftClicked()) にして、シーンの領域が左クリックされたときのみ、円を追加するようにします。

# include <Siv3D.hpp>

void Main()
{
    // 円の半径
    constexpr double circleR = 24.0;

    // 円の色
    constexpr ColorF circleColor(0.4, 0.8, 0.6);

    // 円の中心座標を格納する配列
    Array<Vec2> circles;

    while (System::Update())
    {
        // もしシーンが左クリックされたら
        if (Scene::Rect().leftClicked())
        {
            // 配列に円の中心座標を追加
            circles << Cursor::Pos();
        }

        // 配列内の円の中心座標を使って円を描画
        for (const auto& circle : circles)
        {
            Circle(circle, circleR).draw(circleColor);
        }
    }
}

4. 円ごとに色を変える

Vec2Circle 型は色の情報を持てないので、位置の情報と合わせて ColorF も持つ新しい構造体 CircleItem を作ります。

# include <Siv3D.hpp>

struct CircleItem
{
    // 円の半径 (定数)
    static constexpr double CircleR = 24.0;

    // 円の中心座標
    Vec2 center;

    // 円の色
    ColorF color;

    // デフォルトコンストラクタ
    CircleItem() = default;

    // 座標、色を初期化するコンストラクタ
    CircleItem(const Vec2& _center, const ColorF& _color)
        : center(_center)
        , color(_color) {}

    // 円を描画するメンバ関数
    void draw() const
    {
        Circle(center, CircleR).draw(color);
    }
};

void Main()
{
    // CircleItem を格納する配列
    Array<CircleItem> circleItems;

    while (System::Update())
    {
        // もしシーンが左クリックされたら
        if (Scene::Rect().leftClicked())
        {
            // 配列に CircleItem を追加
            circleItems.emplace_back(Cursor::Pos(), RandomColorF());
        }

        // 配列内の CircleItem を描画
        for (const auto& circleItem : circleItems)
        {
            circleItem.draw();
        }
    }
}

5. Main を短く

Main() 関数内の記述を減らすために、管理用のクラス CircleItemManager を作ります。

# include <Siv3D.hpp>

struct CircleItem
{
    // 円の半径 (定数)
    static constexpr double CircleR = 24.0;

    // 円の中心座標
    Vec2 center;

    // 円の色
    ColorF color;

    // デフォルトコンストラクタ
    CircleItem() = default;

    // 座標、色を初期化するコンストラクタ
    CircleItem(const Vec2& _center, const ColorF& _color)
        : center(_center)
        , color(_color) {}

    // 円を描画するメンバ関数
    void draw() const
    {
        Circle(center, CircleR).draw(color);
    }
};

class CircleItemManager
{
private:

    // CircleItem を格納する配列
    Array<CircleItem> m_circleItems;

public:

    // デフォルトコンストラクタ
    CircleItemManager() = default;

    // 新しい CircleItem を追加するメンバ関数
    void add(const Vec2& center, const ColorF& color)
    {
        // 配列に CircleItem を追加
        m_circleItems.emplace_back(center, color);
    }

    // すべての CircleItem を描画するメンバ関数
    void draw() const
    {
        // 配列内の CircleItem を描画
        for (const auto& circleItem : m_circleItems)
        {
            circleItem.draw();
        }
    }
};

void Main()
{
    CircleItemManager circleItemManager;

    while (System::Update())
    {
        // もしシーンが左クリックされたら
        if (Scene::Rect().leftClicked())
        {
            // 追加
            circleItemManager.add(Cursor::Pos(), RandomColorF());
        }

        // 描画
        circleItemManager.draw();
    }
}

6. エフェクトを付ける

円を追加したときにエフェクトを発生させます。

# include <Siv3D.hpp>

struct CircleItem
{
    // 円の半径 (定数)
    static constexpr double CircleR = 24.0;

    // 円の中心座標
    Vec2 center;

    // 円の色
    ColorF color;

    // デフォルトコンストラクタ
    CircleItem() = default;

    // 座標、色を初期化するコンストラクタ
    CircleItem(const Vec2& _center, const ColorF& _color)
        : center(_center)
        , color(_color) {}

    // 円を描画するメンバ関数
    void draw() const
    {
        Circle(center, CircleR).draw(color);
    }
};

class CircleItemManager
{
private:

    // CircleItem を格納する配列
    Array<CircleItem> m_circleItems;

    // エフェクト
    Effect m_effect;

public:

    // デフォルトコンストラクタ
    CircleItemManager() = default;

    // 新しい CircleItem を追加するメンバ関数
    void add(const Vec2& center, const ColorF& color)
    {
        // 配列に CircleItem を追加
        m_circleItems.emplace_back(center, color);

        // エフェクトを追加
        m_effect.add([center](double t)
        {
            const double e = EaseOutExpo(t);
            Circle(center, CircleItem::CircleR + e * 30)
                .drawFrame(0, 4 * e, ColorF(1.0, (1 - e)));
            return t < 1.0;
        });
    }

    // すべての CircleItem とエフェクトを描画するメンバ関数
    void draw() const
    {
        // エフェクトを更新・描画
        m_effect.update();

        // 配列内の CircleItem を描画
        for (const auto& circleItem : m_circleItems)
        {
            circleItem.draw();
        }
    }
};

void Main()
{
    CircleItemManager circleItemManager;

    while (System::Update())
    {
        // もしシーンが左クリックされたら
        if (Scene::Rect().leftClicked())
        {
            // 追加
            circleItemManager.add(Cursor::Pos(), RandomColorF());
        }

        // 描画
        circleItemManager.draw();
    }
}