XNAメモ – 小規模な2Dパーティクルエフェクトの実装1

Posted: 2012年2月8日 カテゴリー: プログラミングメモ, XNA, XNAメモ
タグ:, , , , , , , , ,

今回は小規模なパーティクルシステムの実装についてメモります。先に書いておきますが、今回(と多分次回)作るシステムは簡単に実装するのを目的として作るので、柔軟性、拡張性、そして速度はかなり微妙です。なので、もしこれを参考にする事があれば、自分が使い易いように変更する事をオススメします。

まず、パーティクルを管理するクラスを作ります。ランダムの値を取る必要がある場合が多いので、Randomも保存してあります。

class ParticleEmitter
{
protected List<Particle> m_particles;
protected Random m_rand;

public ParticleEmitter()
{
m_particles = new List<Particle>();
m_rand = new Random();
}

次に、1個のパーティクルを表すクラスを作ります。このクラスはParticleEmitterクラス内で宣言し、ParticleEmitterクラスと、継承したクラスだけが使う使用にしています。コンストラクタ内では、前に紹介した角度から向きを計算する方法を使用し、Vector2の向きを獲得しています。


protected class Particle
{
public Texture2D m_texture;    //描画するテクスチャア
public Vector2   m_position;   // 位置
public Vector2   m_direction;  // 進む向き
public Vector2   m_origin;     // テクスチャアの原点
public float     m_duration;   // 寿命
public float     m_scale;      // 現在の大きさ
public float     m_shrinkRate; // 収縮する速度
public float     m_speed;      // 移動速度
public bool      m_isActive;   // 生きているかどうかのフラグ
public Color     m_colour;     // 色

public Particle(Texture2D tex, Vector2 pos, float speed, float angle, float scale, float shrinkRate, float duration, Color colour)
{
m_texture = tex;
m_position = pos;
m_scale = scale;
m_shrinkRate = shrinkRate;
m_isActive = true;
m_duration = duration;
m_colour = colour;
m_speed = speed;
m_origin = new Vector2(tex.Width / 2, tex.Height / 2);

// 角度をラジアンに変更する
angle = MathHelper.ToRadians(angle);

// 角度から、向きを獲得する
Vector2 up = new Vector2(0, -1.0f);
Matrix rot = Matrix.CreateRotationZ(angle);
m_direction = Vector2.Transform(up, rot);
}

次に、パーティクルのUpdate()メソッド実装します。今回する事は、コンストラクタ内で獲得した向きに向かって移動、小さくする、そして設定された寿命を越すか、大きさが0以下になった場合、破棄するフラグを立てます。


public void Update(float delta)
{
// パーティクルを移動する
m_position += m_direction * m_speed * delta;

// パーティクルを小さくする
m_scale -= m_shrinkRate * delta;

// 寿命を減らす
m_duration -= delta;

// 大きさが小さすぎるか、寿命が尽きたら
if (m_scale <= 0.0f || m_duration <= 0.0f)
{
// パーティクルを破棄する
m_isActive = false;
m_position = new Vector2(-100, -100);
}
}

後は描画のメソッドだけです。これは単純に描画するだけです。


public void Draw(SpriteBatch sp)
{
sp.Draw(m_texture, m_position, null, m_colour, 0, m_origin, m_scale, SpriteEffects.None, 0);
}

Particleクラスはこれで出来ましたので、次にParticleEmitterクラスの続きを実装します。まず、保存しているパーティクルを全て破棄するメソッドを作ります。これは単純にリストからClear()メソッドを呼ぶだけです。


public void Clear()
{
m_particles.Clear();
}

全てのパーティクルの更新と、フラグが立っている場合破棄をするメソッドを作ります。


public void Update(float delta)
{
// 破棄されているパーティクル
List<Particle> toRemove = new List<Particle>();

for (int i = 0; i < m_particles.Count; i++)
{
// パーティクルの更新
m_particles[i].Update(delta);

// もし現在のパーティクルが破棄されていた場合
if (!m_particles[i].m_isActive)
{
// 破棄するリストに追加
toRemove.Add(m_particles[i]);
}
}

for (int i = 0; i < toRemove.Count; i++)
{
// パーティクルをリストから除外する
m_particles.Remove(toRemove[i]);
}
}

今回の実装方法は、更新するループ内では破棄するパーティクルを除外せず、後で除外する用のリストに追加し、全てのパーティクルが更新し終わってから破棄されたパーティクルを除外しています。この実装方法ではなく、最初のループ内でリストから除外しても問題は特に無いです。

次は、全てのパーティクルを描画するメソッドを作ります。これはシンプルです。


public void Draw(SpriteBatch sp)
{
for (int i = 0; i < m_particles.Count; i++)
{
m_particles[i].Draw(sp);
}
}

最後に、実際にパーティクルを追加するメソッドです。このメソッドはパラメータを弄る事で少しの変化を付けられるように、渡す値が結構多くなります(次回のメモとかではさらに増える予定です)。

渡している値は順番で、テクスチャア、位置、大きさ、収縮するのに使う値、寿命、生成する量、最高速度、色です。

今回は基本的に、ランダムな向きに、ランダムな速度で移動するパーティクルを作る使用にしています。


public void Emmit(Texture2D tex, Vector2 pos, float scale, float shrinkRate,
float duration, int amount, int maxSpeed, Color colour)
{
Particle p;

for (int i = 0; i < amount; i++)
{
// ランダムな角度を獲得する
int angle = m_rand.Next(0, 360);

// ランダムな速度を獲得する
float speed = m_rand.Next(1, maxSpeed);

// 新しいパーティクルを作る
p = new Particle(tex, pos, speed, angle, scale, shrinkRate, duration, colour);

// パーティクルを足す
m_particles.Add(p);
}
}

後は実際に使うだけです。

protected override void Update(GameTime gameTime)
{
float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;

// スペースキーが押されたら
if (Input.IsPressed(Keys.Space))
{
// パーティクルを作る
emitter.Emmit(particleTex, new Vector2(300, 300), 0.5f, 1.0f, 1.5f, 100, 200, Color.Gold);
}

if (Input.IsPressed(Keys.Z))
{
float scale = 0.5f;
float shrink = 0.5f;
int speed = 600;
emitter.Emmit(particleTex, new Vector2(300, 300), scale, shrink, 2.0f, 40, speed, Color.Salmon);
emitter.Emmit(particleTex, new Vector2(300, 300), scale, shrink, 2.0f, 40, speed, Color.Pink);
emitter.Emmit(particleTex, new Vector2(300, 300), scale, shrink, 2.0f, 40, speed, Color.Crimson);
}

// パーティクルの更新
emitter.Update(delta);

base.Update(gameTime);
}

これで実装後はこんな感じになります(使う画像によって見栄えは変わりますが)

以上です。Emmit()メソッドの値を弄ると少し違う見かけも物も作れます。

後1,2回これに関してメモする予定ですので、これで実装終了という訳ではありません。

コメント
  1. […] XNAメモ – 小規模な2Dパーティクルエフェクトの実装1 […]

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中