#パーティクルシステムとは
パーティクル・システム(英: particle system)はコンピュータグラフィック技術のひとつで、運動する粒子により構成される事象を表現するために使われる。一般にパーティクル・システムを用いて模擬される事象の例としては、炎、爆発、煙、流水、火花、落葉、雲、霧、雪、埃、流星、毛髪、毛皮、草地、あるいは光跡や呪文の視覚効果などが挙げられる。Wikipediaより
公式リファレンスはこちら
https://siv3d.github.io/ja-jp/reference/2d-particle/
#サンプル
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());
}
}
#使い方
大まかな流れとしては、
- ParticleSystem2Dクラスのインスタンスを作る
- 各種設定をする
- ゲームループ内でupdate()やdraw()などを呼び出す
といった感じです。
##各種設定
###ParticleSystem2D
項目 | 型 | 説明 |
---|---|---|
setPosition | Vec2 | パーティクル源の位置 |
setForce | Vec2 | パーティクルにかかる加速度 |
setEmitter | Emitter | 後述 |
setTexture | Texture | パーティクルのテクスチャ |
setParameters | ParticleSystem2DParameters | 後述 |
パーティクルのテクスチャを変更したときの図 | ||
###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 | パーティクル源の丸みの半径 |