トレーニング | 円を配置する¶
クリックで画面上に円を配置していくプログラムを作ります。
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. 円ごとに色を変える¶
Vec2
や Circle
型は色の情報を持てないので、位置の情報と合わせて 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();
}
}