Siv3D | 今すぐ使えるエフェクト集

2020/09/27に公開

1. 色の付いたバブル

# include <Siv3D.hpp> // OpenSiv3D v0.6.2

struct BubbleEffect : IEffect
{
	struct Bubble
	{
		Vec2 offset;
		double startTime;
		double scale;
		ColorF color;
	};

	Vec2 m_pos;

	Array<Bubble> m_bubbles;

	BubbleEffect(const Vec2& pos, double baseHue)
		: m_pos{ pos }
	{
		for (int32 i = 0; i < 8; ++i)
		{
			Bubble bubble{
				.offset = RandomVec2(Circle{30}),
				.startTime = Random(-0.3, 0.1), // 登場の時間差
				.scale = Random(0.1, 1.2),
				.color = HSV{ baseHue + Random(-30.0, 30.0) }
			};
			m_bubbles << bubble;
		}
	}

	bool update(double t) override
	{
		for (const auto& bubble : m_bubbles)
		{
			const double t2 = (bubble.startTime + t);

			if (not InRange(t2, 0.0, 1.0))
			{
				continue;
			}

			const double e = EaseOutExpo(t2);

			Circle{ (m_pos + bubble.offset + (bubble.offset * 4 * t)), (e * 40 * bubble.scale) }
				.draw(ColorF{ bubble.color, 0.15 })
				.drawFrame((30.0 * (1.0 - e) * bubble.scale), bubble.color);
		}

		return (t < 1.3);
	}
};

void Main()
{
	Effect effect;

	while (System::Update())
	{
		if (MouseL.down())
		{
			effect.add<BubbleEffect>(Cursor::PosF(), Random(0.0, 360.0));
		}

		{
			const ScopedRenderStates2D blend{ BlendState::Additive };
			effect.update();
		}
	}
}
v0.4.3
# include <Siv3D.hpp> // OpenSiv3D v0.4.3

struct BubbleEffect : IEffect
{
    struct Bubble
    {
        Vec2 offset;
        double startTime;
        double scale;
        ColorF color;
    };

    Vec2 m_pos;

    Array<Bubble> m_bubbles;

    BubbleEffect(const Vec2& pos, double baseHue)
        : m_pos(pos)
    {
        for (int32 i = 0; i < 8; ++i)
        {
            Bubble bubble{
                .offset = RandomVec2(Circle(30)),
                .startTime = Random(-0.3, 0.1),
                .scale = Random(0.1, 1.2),
                .color = HSV(baseHue + Random(-30.0, 30.0))
            };
            m_bubbles << bubble;
        }
    }

    bool update(double t) override
    {
        for (const auto& bubble : m_bubbles)
        {
            const double t2 = (bubble.startTime + t);

            if (!InRange(t2, 0.0, 1.0))
            {
                continue;
            }

            const double e = EaseOutExpo(t2);

            Circle(m_pos + bubble.offset + (bubble.offset * 4 * t), e * 40 * bubble.scale)
                .draw(ColorF(bubble.color, 0.15))
                .drawFrame(30.0 * (1.0 - e) * bubble.scale, bubble.color);
        }

        return (t < 1.3);
    }
};

void Main()
{
    Window::Resize(1280, 720);
    Effect effect;

    while (System::Update())
    {
        if (MouseL.down())
        {
            effect.add<BubbleEffect>(Cursor::PosF(), Random(0.0, 360.0));
        }

        {
            ScopedRenderStates2D blend(BlendState::Additive);
            effect.update();
        }
    }
}

2. 水平フレア

# include <Siv3D.hpp> // OpenSiv3D v0.6.2

struct VerticalFlare : IEffect
{
	Vec2 m_pos;

	Texture m_texture;

	VerticalFlare(const Vec2& pos, const Texture& texture)
		: m_pos{ pos }
		, m_texture{ texture } {}

	bool update(double t) override
	{
		t /= 0.5;
		const double e = EaseInSine(t);
		const double scale = 6.0 * (1.0 - e);
		double s = 1.0;

		for (auto i : step(9))
		{
			m_texture.scaled(scale * Vec2{ s, (1.0 / s) })
				.drawAt(m_pos, HSV{ (120 * i), 0.4 });
			s *= 1.25;
		}

		return (t < 1.0);
	}
};

void Main()
{
	Window::Resize(1280, 720);
	Effect effect;
	const Texture texture{ U"example/particle.png", TextureDesc::Mipped };

	while (System::Update())
	{
		if (MouseL.down())
		{
			effect.add<VerticalFlare>(Cursor::PosF(), texture);
		}

		{
			const ScopedRenderStates2D blend{ BlendState::Additive };
			effect.update();
		}
	}
}
v0.4.3
# include <Siv3D.hpp> // OpenSiv3D v0.4.3

struct VerticalFlare : IEffect
{
	Vec2 m_pos;

	Texture m_texture;

	VerticalFlare(const Vec2& pos, const Texture& texture)
		: m_pos(pos)
		, m_texture(texture) {}

	bool update(double t) override
	{
		t /= 0.5;
		const double e = EaseInSine(t);
		const double scale = 6.0 * (1.0 - e);
		double s = 1.0;

		for (auto i : step(9))
		{
			m_texture.scaled(scale * Vec2(s, 1.0 / s))
				.drawAt(m_pos, HSV(120 * i, 0.4));
			s *= 1.25;
		}

		return (t < 1.0);
	}
};

void Main()
{
	Window::Resize(1280, 720);
	Effect effect;

	const Texture texture(U"example/particle.png");

	while (System::Update())
	{
		if (MouseL.down())
		{
			effect.add<VerticalFlare>(Cursor::PosF(), texture);
		}

		{
			ScopedRenderStates2D blend(BlendState::Additive);
			effect.update();
		}
	}
}

3. 飛び散る星

# include <Siv3D.hpp> // OpenSiv3D v0.6.2

struct StarEffect : IEffect
{
	static constexpr Vec2 Gravity{ 0, 160 };

	struct Star
	{
		Vec2 start;
		Vec2 velocity;
		ColorF color;
	};

	Array<Star> m_stars;

	StarEffect(const Vec2& pos, double baseHue)
	{
		for (int32 i = 0; i < 6; ++i)
		{
			const Vec2 velocity = RandomVec2(Circle{ 60 });
			Star star{
				.start = (pos + velocity),
				.velocity = velocity,
				.color = HSV{ baseHue + Random(-20.0, 20.0) },
			};
			m_stars << star;
		}
	}

	bool update(double t) override
	{
		t /= 0.4;

		for (auto& star : m_stars)
		{
			const Vec2 pos = star.start
				+ star.velocity * t + 0.5 * t * t * Gravity;

			const double angle = (pos.x * 3_deg);

			Shape2D::Star((30 * (1.0 - t)), pos, angle)
				.draw(star.color);
		}

		return (t < 1.0);
	}
};

void Main()
{
	Effect effect;
	Circle circle{ Scene::Center(), 30 };
	double baseHue = 180.0;

	while (System::Update())
	{
		if (circle.mouseOver())
		{
			Cursor::RequestStyle(CursorStyle::Hand);
		}

		if (circle.leftClicked())
		{
			effect.add<StarEffect>(Cursor::PosF(), baseHue);
			circle.center = RandomVec2(Scene::Rect().stretched(-80));
			baseHue = Random(0.0, 360.0);
		}

		circle.draw(HSV{ baseHue });
		effect.update();
	}
}
v0.4.3
# include <Siv3D.hpp> // OpenSiv3D v0.4.3

struct StarEffect : IEffect
{
	static constexpr Vec2 Gravity{ 0, 160 };

	struct Star
	{
		Vec2 start;
		Vec2 velocity;
		ColorF color;
	};

	Array<Star> m_stars;

	StarEffect(const Vec2& pos, double baseHue)
	{
		for (int32 i = 0; i < 6; ++i)
		{
			const Vec2 velocity = RandomVec2(Circle(60));
			Star star{
				.start = pos + velocity,
				.velocity = velocity,
				.color = HSV(baseHue + Random(-20.0, 20.0)),
			};
			m_stars << star;
		}
	}

	bool update(double t) override
	{
		t /= 0.4;

		for (auto& star : m_stars)
		{
			const Vec2 pos = star.start
				+ star.velocity * t + 0.5 * t * t * Gravity;

			const double angle = pos.x * 5_deg;

			Shape2D::Star(30 * (1 - t), pos, angle)
				.draw(star.color);
		}

		return (t < 1.0);
	}
};

void Main()
{
	Window::Resize(1280, 720);
	Effect effect;
	Circle circle(Scene::Center(), 30);
	double baseHue = 180.0;

	while (System::Update())
	{
		if (circle.mouseOver())
		{
			Cursor::RequestStyle(CursorStyle::Hand);
		}

		if (circle.leftClicked())
		{
			effect.add<StarEffect>(Cursor::PosF(), baseHue);
			circle.center = RandomVec2(Scene::Rect().stretched(-80));
			baseHue = Random(0.0, 360.0);
		}

		circle.draw(HSV(baseHue));
		effect.update();
	}
}

4. クリック時のパーティクルエフェクト

# include <Siv3D.hpp> // OpenSiv3D v0.6.2

struct TouchEffect : IEffect
{
	struct Particle
	{
		Vec2 velocity;
		Vec2 start;
		double r;
		double angle;
		bool cw;
		ColorF color;
	};

	struct Star
	{
		Vec2 velocity;
		Vec2 start;
		double angle;
		double scale;
		ColorF color;
	};

	Vec2 m_pos;

	Array<Particle> m_particles;

	Array<Star> m_stars;

	explicit TouchEffect(const Vec2& pos)
		: m_pos{ pos }
	{
		for (int32 i = 0; i < 200; ++i)
		{
			const Vec2 velocoty = RandomVec2(28.0);
			Particle particle{
				.velocity = velocoty,
				.start = velocoty,
				.r = Random(6.0, 12.0),
				.angle = Random(360_deg),
				.cw = RandomBool(),
				.color = HSV{ Random(50.0, 70.0), 0.4, 1.0 },
			};
			m_particles << particle;
		}

		for (int32 i = 0; i < 8; ++i)
		{
			const Vec2 velocoty = RandomVec2(28.0);
			Star star{
				.velocity = velocoty,
				.start = (velocoty + RandomVec2(2.0)),
				.angle = Random(360_deg),
				.scale = Random(0.6, 1.4),
				.color = HSV{ Random(50.0, 70.0), 0.4, 1.0 },
			};
			m_stars << star;
		}
	}

	bool update(double t) override
	{
		t /= 0.45;

		const double r = (30 + t * 30);
		const ColorF outer = HSV{ 180, 0.8, 1.0, 0.0 };
		const ColorF inner = HSV{ 180, 0.8, 1.0, (0.5 * (1.0 - t)) };

		Circle{ m_pos, r }
			.drawFrame(10, 0, outer, inner)
			.drawFrame(0, 10, inner, outer);

		for (const auto& particle : m_particles)
		{
			const Vec2 pos = m_pos
				+ particle.start
				+ Circular(particle.r, particle.angle + t * 120_deg * (particle.cw ? 1 : -1))
				+ (particle.velocity * t - 0.5 * t * t * particle.velocity);
			const double rOuter = (1.0 * (1.0 - t) * 2);
			const double rInner = (0.8 * (1.0 - t) * 2);

			Shape2D::NStar(2, rOuter, rInner, pos, particle.angle)
				.draw(particle.color);
		}

		for (const auto& star : m_stars)
		{
			const Vec2 pos = m_pos
				+ star.start
				+ (star.velocity * t - 0.5 * t * t * star.velocity);
			const double rOuter = (12 * (1.0 - t) * star.scale);
			const double rInner = (4 * (1.0 - t) * star.scale);
			const double angle = (star.angle + t * 90_deg);

			Shape2D::NStar(4, rOuter, rInner, pos, angle)
				.draw(star.color);
		}

		return (t < 1.0);
	}
};

void Main()
{
	Effect effect;

	while (System::Update())
	{
		if (MouseL.down())
		{
			effect.add<TouchEffect>(Cursor::PosF());
		}

		{
			const ScopedRenderStates2D blend{ BlendState::Additive };
			effect.update();
		}
	}
}
v0.4.3
# include <Siv3D.hpp> // OpenSiv3D v0.4.3

struct ParticleClickEffect : IEffect
{
	struct Particle
	{
		Vec2 velocity;
		Vec2 start;
		double r;
		double angle;
		bool cw;
		ColorF color;
	};

	struct Star
	{
		Vec2 velocity;
		Vec2 start;
		double angle;
		double scale;
		ColorF color;
	};

	Vec2 m_pos;

	Array<Particle> m_particles;

	Array<Star> m_stars;

	ParticleClickEffect(const Vec2& pos)
		: m_pos(pos)
	{
		for (int32 i = 0; i < 200; ++i)
		{
			const Vec2 velocoty = RandomVec2(28.0);
			Particle particle{
				.velocity = velocoty,
				.start = velocoty,
				.r = Random(6.0, 12.0),
				.angle = Random(360_deg),
				.cw = RandomBool(),
				.color = HSV(Random(50.0, 70.0), 0.4, 1.0),
			};
			m_particles << particle;
		}

		for (int32 i = 0; i < 8; ++i)
		{
			const Vec2 velocoty = RandomVec2(28.0);
			Star star{
				.velocity = velocoty,
				.start = velocoty + RandomVec2(2.0),
				.angle = Random(360_deg),
				.scale = Random(0.6, 1.4),
				.color = HSV(Random(50.0, 70.0), 0.4, 1.0),
			};
			m_stars << star;
		}
	}

	bool update(double t) override
	{
		t /= 0.45;

		const double r = 30 + t * 30;
		const ColorF outer = HSV(180, 0.8, 1.0, 0.0);
		const ColorF inner = HSV(180, 0.8, 1.0, 0.5 * (1 - t));

		Circle(m_pos, r)
			.drawFrame(10, 0, outer, inner)
			.drawFrame(0, 10, inner, outer);

		for (const auto& particle : m_particles)
		{
			const Vec2 pos = m_pos
				+ particle.start
				+ Circular(particle.r, particle.angle + t * 120_deg * (particle.cw ? 1 : -1))
				+ (particle.velocity * t - 0.5 * t * t * particle.velocity);

			Shape2D::NStar(2, 1.0 * (1 - t) * 2, 0.8 * (1 - t) * 2, pos, particle.angle)
				.draw(particle.color);
		}

		for (const auto& star : m_stars)
		{
			const Vec2 pos = m_pos
				+ star.start
				+ (star.velocity * t - 0.5 * t * t * star.velocity);

			Shape2D::NStar(4, 12 * (1 - t) * star.scale, 4 * (1 - t) * star.scale,
				pos, star.angle + t * 90_deg)
				.draw(star.color);
		}

		return (t < 1.0);
	}
};

void Main()
{
	Window::Resize(1280, 720);
	Effect effect;

	while (System::Update())
	{
		if (MouseL.down())
		{
			effect.add<ParticleClickEffect>(Cursor::PosF());
		}

		{
			ScopedRenderStates2D blend(BlendState::Additive);
			effect.update();
		}
	}
}

参考資料

Discussion