LoginSignup
6
2

More than 3 years have passed since last update.

OpenSiv3Dの2Dパーティクルシステムの使い方

Last updated at Posted at 2019-12-07

パーティクルシステムとは

パーティクル・システム(英: particle system)はコンピュータグラフィック技術のひとつで、運動する粒子により構成される事象を表現するために使われる。一般にパーティクル・システムを用いて模擬される事象の例としては、炎、爆発、煙、流水、火花、落葉、雲、霧、雪、埃、流星、毛髪、毛皮、草地、あるいは光跡や呪文の視覚効果などが挙げられる。Wikipediaより

公式リファレンスはこちら
https://siv3d.github.io/ja-jp/reference/2d-particle/

Siv3DAdC2019.gif

サンプル

OpenSiv3Dのバージョンは0.4.2です。

これは公式リファレンスのサンプルを自分用に調整したコードです。
(gist)

//based on https://siv3d.github.io/ja-jp/reference/2d-particle/
# include <Siv3D.hpp>

const String siv3d_kun_path = U"example/siv3d-kun.png";
Polygon CreateSiv3D_kunPolygon()
{
    return Image(siv3d_kun_path)
        .alphaToPolygonCentered().simplified(1);
}

auto CreateParticleTexture()
{
    Image image{ 128,128 };
    Shape2D::NStar(5, 64, 32, image.size() / 2).asPolygon().overwrite(image, Palette::White);
    return Texture{ image };
};

void Main()
{
    Window::Resize(1280, 720);
    bool debugMode = false;
    bool whiteBackground = false;

    const std::array<BlendState, 4> blends = {
        BlendState::Default, BlendState::Additive, BlendState::Opaque, BlendState::Subtractive
    };
    size_t blendIndex = 0;

    CircleEmitter2D circleEmitter;
    ArcEmitter2D arcEmitter;
    RectEmitter2D rectEmitter;
    PolygonEmitter2D polygonEmitter(CreateSiv3D_kunPolygon());

    enum EmitterType : size_t {
        Circle, Arc, Rect, Polygon,
    };
    size_t emitterIndex = EmitterType::Arc;

    Vec2 position{ 300, 340 };
    Vec2 force{ 0.0, 0.0 };

    ParticleSystem2D particleSystem(position, force);
    particleSystem.setEmitter(arcEmitter);
    particleSystem.setTexture(CreateParticleTexture());
    particleSystem.prewarm();

    ParticleSystem2DParameters parameters;
    HSV startColor{ ColorF(1.0) };

    const Texture texture = Texture(siv3d_kun_path);
    bool drawTexture = false;

    while (System::Update())
    {
        SimpleGUI::CheckBox(debugMode, U"Debug", Vec2(40, 660), 120);
        SimpleGUI::CheckBox(whiteBackground, U"White", Vec2(160, 660), 120);
        SimpleGUI::CheckBox(drawTexture, U"Texture", Vec2(280, 660), 140, (emitterIndex == EmitterType::Polygon));
        Scene::SetBackground(whiteBackground ? Color(250, 252, 255) : Palette::DefaultBackground);

        {//particleSystem,parametersの設定
            const int32 x = 560, labelw = 120, sliderw = 200;
            SimpleGUI::Slider(U"Rate", parameters.rate, 1.0, 500.0, { x, 20 }, labelw, sliderw);
            SimpleGUI::Slider(U"Max", parameters.maxParticles, 50.0, 2500.0, Vec2(x, 60), labelw, sliderw);
            SimpleGUI::Slider(U"LifeTime", parameters.startLifeTime, 0.0, 5.0, Vec2(x, 100), labelw, sliderw);
            SimpleGUI::Slider(U"Speed", parameters.startSpeed, 0.0, 320.0, Vec2(x, 140), labelw, sliderw);
            SimpleGUI::ColorPicker(startColor, Vec2(x, 180));
            SimpleGUI::Slider(U"Size", parameters.startSize, 0.0, 150.0, Vec2(x, 300), labelw, sliderw);
            SimpleGUI::Slider(U"Rotation", parameters.startRotationDeg, -180, 180, Vec2(x, 340), labelw, sliderw);
            SimpleGUI::Slider(U"AngularVel", parameters.startAngularVelocityDeg, -720, 720, Vec2(x, 380), labelw, sliderw);
            SimpleGUI::Slider(U"Force X", force.x, -320.0, 320.0, Vec2(x, 420), labelw, sliderw);
            SimpleGUI::Slider(U"Force Y", force.y, -320, 320.0, Vec2(x, 460), labelw, sliderw);
            SimpleGUI::RadioButtons(blendIndex, { U"Default", U"Additive", U"Opaque", U"Subtractive" }, Vec2(x, 500), 320);

            SimpleGUI::CheckBox(parameters.randomStartRotation, U"randomRotation", Vec2(x, 660), 220);
            SimpleGUI::CheckBox(parameters.randomAngularVelocity, U"randomAngularVelocity", Vec2(x + 200, 660), 280);

            parameters.blendState = blends[blendIndex];
            parameters.startColor = startColor;
            particleSystem.setParameters(parameters);
            particleSystem.setForce(force);

            if (MouseR.pressed())
                particleSystem.setPosition(position = Cursor::Pos());
        }

        {//emitterの設定
            const int32 x = 900, labelw = 120, sliderw = 200;

            bool emitterChanged = false;
            emitterChanged = SimpleGUI::RadioButtons(emitterIndex, { U"Circle", U"Arc", U"Rect", U"Polygon" }, Vec2(x, 20), 360);

            switch (emitterIndex)
            {
            case EmitterType::Circle:
                emitterChanged |= SimpleGUI::Slider(U"Source Radius", circleEmitter.sourceRadius, 0.0, 40.0, Vec2(x, 180), 160, 200);
                emitterChanged |= SimpleGUI::Slider(U"R", circleEmitter.r, 0.0, 320.0, Vec2(x, 220), 160, 200);
                emitterChanged |= SimpleGUI::CheckBox(circleEmitter.randomDirection, U"Random Direction", Vec2(x, 260), 360);
                emitterChanged |= SimpleGUI::CheckBox(circleEmitter.fromShell, U"From Shell", Vec2(x, 300), 300);

                if (emitterChanged) // setEmitter は重い処理なので、変更があった時だけ
                    particleSystem.setEmitter(circleEmitter);
                break;

            case EmitterType::Arc:
                emitterChanged |= SimpleGUI::Slider(U"Source Radius", arcEmitter.sourceRadius, 0.0, 40.0, Vec2(x, 180), 160, 200);
                emitterChanged |= SimpleGUI::Slider(U"R", arcEmitter.r, 0.0, 320.0, Vec2(x, 220), 160, 200);
                emitterChanged |= SimpleGUI::Slider(U"Direction", arcEmitter.direction, -180, 180, Vec2(x, 260), 160, 200);
                emitterChanged |= SimpleGUI::Slider(U"Angle", arcEmitter.angle, 0.0, 360, Vec2(x, 300), 160, 200);
                emitterChanged |= SimpleGUI::CheckBox(arcEmitter.randomDirection, U"Random Direction", Vec2(x, 340), 360);
                emitterChanged |= SimpleGUI::CheckBox(arcEmitter.fromShell, U"From Shell", Vec2(x, 380), 360);

                if (emitterChanged) // setEmitter は重い処理なので、変更があった時だけ
                    particleSystem.setEmitter(arcEmitter);
                break;

            case EmitterType::Rect:
                emitterChanged |= SimpleGUI::Slider(U"Source Radius", rectEmitter.sourceRadius, 0.0, 40.0, Vec2(x, 180), 160, 200);
                emitterChanged |= SimpleGUI::Slider(U"Width", rectEmitter.width, 0, 720, Vec2(x, 220), 160, 200);
                emitterChanged |= SimpleGUI::Slider(U"Height", rectEmitter.height, 0, 720, Vec2(x, 260), 160, 200);
                emitterChanged |= SimpleGUI::CheckBox(rectEmitter.randomDirection, U"Random Direction", Vec2(x, 300), 360);
                emitterChanged |= SimpleGUI::CheckBox(rectEmitter.fromShell, U"From Shell", Vec2(x, 340), 360);

                if (emitterChanged) // setEmitter は重い処理なので、変更があった時だけ
                    particleSystem.setEmitter(rectEmitter);
                break;

            case EmitterType::Polygon:
                if (emitterChanged) // setEmitter は重い処理なので、変更があった時だけ
                    particleSystem.setEmitter(polygonEmitter);
                break;
            }
        }

        particleSystem.update();

        if (debugMode)
            particleSystem.drawDebug();
        else
            particleSystem.draw();

        if (emitterIndex == EmitterType::Polygon && drawTexture)
            texture.drawAt(position);

        ClearPrint();
        Print << U"{} particles"_fmt(particleSystem.num_particles());
    }
}

使い方

大まかな流れとしては、
1. ParticleSystem2Dクラスのインスタンスを作る
2. 各種設定をする
3. ゲームループ内でupdate()やdraw()などを呼び出す
といった感じです。

各種設定

ParticleSystem2D

項目 説明
setPosition Vec2 パーティクル源の位置
setForce Vec2 パーティクルにかかる加速度
setEmitter Emitter 後述
setTexture Texture パーティクルのテクスチャ
setParameters ParticleSystem2DParameters 後述

パーティクルのテクスチャを変更したときの図
20191121-191658-123.png

ParticleSystem2DParameters

項目 説明
rate double パーティクルの生成頻度[/秒]
maxParticles double パーティクルの生成数(古いものから消える)
startLifeTime double パーティクルの生存時間[秒]
startSpeed double パーティクルの初速
startColor ColorF パーティクルの初期の色
colorOverLifeTimeFunc Float4(Float4,float,float) 色(初期色(ColorF),総寿命,残り寿命)
startSize double パーティクルの初期のサイズ
sizeOverLifeTimeFunc float(float,float,float) サイズ(初期サイズ,総寿命,残り寿命)
startRotationDeg double パーティクルの初期の回転[度]
randomStartRotation bool ↑をランダムにする(±180の間)
startAngularVelocityDeg double パーティクルの回転速度[度/sec]
randomAngularVelocity bool ↑をランダムにする(±Abs(↑の値)の間)
blendState BlendState ブレンドステート(加算合成とか)

Emitter

パーティクル源の形状などに関する部分です。現在

  • CircleEmitter2D
  • ArcEmitter2D
  • RectEmitter2D
  • PolygonEmitter2D

の4種があります。

Polygon以外のEmitterでできる設定

項目 説明
各図形の設定 ---- 大きさや角度など
randomDirection bool trueのときはランダム falseのときパーティクルの発生方向が図形の中心→外側の方向になる
fromShell bool trueのときパーティクル源の芯が図形のふちになる falseのときは図形全体

4種のEmitter全てでできる設定

項目 説明
sourceRadius double パーティクル源の丸みの半径
6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2