数つなぎ - Siv3D/Reference-JP GitHub Wiki

数つなぎ
# include <Siv3D.hpp>

const double circleR = 30;

struct Ripple : IEffect
{
    Vec2 m_pos;

    Ripple(const Vec2& pos) : m_pos(pos) {}

    bool update(double t) override
    {
        Circle(m_pos, circleR + t * 200).drawFrame(2, 0, ColorF(0.2, 0.5, 1.0, 1.0 - t*2.5));
        return t < 0.4;
    }
};

struct Bubble
{
    Circle circle;
    int32 number;
    bool hasRing = false;

    void draw() const
    {
        circle.draw();

        if (hasRing)
        {
            circle.drawFrame(2, 0, Color(60, 160, 250));
        }

        FontAsset(L"bubble")(number + 1).drawCenter(circle.center, Color(10));
    }
};

bool CheckBubbles(const Array<Bubble>& bubbles)
{
    for (auto i : step(bubbles.size()))
    {
        for (auto k : step(bubbles.size()))
        {
            if (i != k && bubbles[i].circle.stretched(5).intersects(bubbles[k].circle.stretched(5)))
            {
                return false;
            }
        }
    }

    return true;
}

Array<Bubble> MakeBubbles(int count)
{
    Array<Bubble> bubbles(count);

    do
    {
        for (auto i : step(count))
        {
            bubbles[i].number = i;
            bubbles[i].circle.set(RandomVec2(Circle(Window::Center(), Window::Width() / 2 - circleR)), circleR);
        }
    } while (!CheckBubbles(bubbles));

    return bubbles;
}

void Main()
{
    Graphics::SetBackground(Palette::White);
    Window::Resize(720, 720);
    FontAsset::Register(L"bubble", 20, Typeface::Light);

    int32 highScore = 0;
    int32 stage = 1;
    int32 targetBubble = 1;
    int32 targetTime = 8000;
    int32 currentBubble = 0;
    Array<Bubble> bubbles = MakeBubbles(targetBubble);

    const Array<uint8> midis{ 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95, 96 };
    Midi::SendMessage(MidiMessage::SetInstrument(0, GMInstrument::Glockenspiel));

    Effect effect;
    Stopwatch stopwatch(true);

    while (System::Update())
    {
        RectF(Window::Width(), static_cast<double>(Window::Height()) * stopwatch.ms() / targetTime).draw(HSV(stage * 30, 0.2, 1.0));

        for (auto& bubble : bubbles)
        {
            if (bubble.number == currentBubble && !bubble.hasRing && bubble.circle.stretched(10).mouseOver)
            {
                bubble.hasRing = true;
                ++currentBubble;
                effect.add<Ripple>(bubble.circle.center);
                Midi::SendMessage(MidiMessage::NoteOn(0, midis[bubble.number]));
            }

            Circular c = Circular(bubble.circle.center.movedBy(-Window::Center()));
            c.theta += bubble.number % 2 ? 0.002 : -0.002;
            bubble.circle.setPos(Vec2(c).movedBy(Window::Center()));
        }

        for (int i = 0; i < currentBubble - 1; ++i)
        {
            Line(bubbles[i].circle.center, bubbles[i + 1].circle.center).draw(2.0, Palette::Orange);
        }

        const bool failed = stopwatch.ms() > targetTime;

        if (currentBubble == targetBubble || failed)
        {
            stage = failed ? 1 : ++stage;
            highScore = Max(highScore, stage);

            Window::SetTitle(L"Stage {} (High score: {})"_fmt, stage, highScore);

            currentBubble = 0;
            targetBubble = Min(stage, 15);
            bubbles = MakeBubbles(targetBubble);
            targetTime = stage <= 15 ? 8000 : 8000 - Min((stage - 15) * 50, 2000);
            stopwatch.restart();
        }

        for (const auto& bubble : bubbles)
        {
            bubble.draw();
        }

        effect.update();
    }
}
⚠️ **GitHub.com Fallback** ⚠️