「エフェクト」タグのついた投稿

このメモでは爆発や、強烈な攻撃などの衝撃の際に、画面が振動して見えるエフェクトの作り方を紹介します。

 

上記の説明だけではわかりにくいので、まずは以下の画像をご覧ください:

通常のゲーム画面

振動エフェクト中のゲーム画面

 

振動のエフェクトは上にある2番目の画像の用に始まり、徐々に薄くなって最終的には元に戻るようになります。

 

では実装に入ります。まず、エフェクトで振動の為に実際に使われる数値などを保存するクラスを作ります。

 

class Blast
{
public float Amount;    // 量
public float Megnitude; // 震度
public Vector2 Center;  // 中心点

 

今回実装するクラスは、簡易化のため、基本的にメンバ変数はpublicにしています。次に、更新処理です。ここでは、徐々に振動を減らすための計算に使用されるAmountメンバ変数を徐々に下げていきます。

 

public void Update(float delta)
{
if (Amount >= 0.0f)
{
Amount -= delta;
}
else if (Amount < 0.0f)
{
Amount = 0.0f;
}
}

 

これでBlastクラスの実装は終わりです。次に、Blastクラスを使用して実際にエフェクトを作製し、描画するクラスを宣言します。

 

static class ShakeManager
{
public static Blast Blast = new Blast();
public static int ShakeStrength = 4;     // エフェクトの濃さ

 

このクラスも、使用しやすいようにstaticクラスにしています。ですが、staticクラスでなくてはいけないということは無いので、staticを外して実装しても問題は無いです。

 

次は、更新処理を実装します。今回は、BlastクラスのUpdate()メソッドを呼ぶだけです。

 

public static void Update(float delta)
{
Blast.Update(delta);
}

 

次に、Blastクラスの値を設定するメソッドを実装します。このメソッドは衝撃のエフェクトを使用し始めたい時に呼びます。

 

public static void SetupBlast(float magnitude, Vector2 center)
{
Blast.Amount    = magnitude;
Blast.Megnitude = magnitude;
Blast.Center    = center;
}

 

今度は描画処理を実装します。振動のエフェクトもこのメソッド内で計算され、描画されます。

 

描画処理のメソッドには、引数の一つとして、ゲーム画面を保存しているテクスチャアを送る必要があります。エフェクトを作製する際、送られてきた画像を元に手を加える事で振動のエフェクトを実装しているので、描画の度に最新のゲーム画面を送る必要があります。

 

public static void Draw(SpriteBatch sp, Texture2D gameContent)
{
if (Blast.Amount > 0.0f)
{
// 設定されている震度の濃さの分だけエフェクトを描画する
for (int i = 0; i < ShakeStrength; i++)
{
// 原点を計算する
Vector2 origin = ShakeManager.Blast.Center / 2;

// 描画位置を計算する
Vector2 pos = Blast.Center - origin;

// アルファ値を計算する
float alpha = 0.35f * (Blast.Amount / Blast.Megnitude);
Color col = new Color(1.0f, 1.0f, 1.0f, alpha);

// 大きさを計算する
float scale = (1.0f + (Blast.Megnitude - Blast.Amount) *
               0.1f + ((float)(i + 1) / 40.0f));

// エフェクトを描画する
sp.Draw(gameContent, pos, null, col, 0.0f,
origin, scale, SpriteEffects.None, 1.0f);
}
}
}

このエフェクトの実装には、実際に弄ってみてよく見える数値を探し、そのまま使用している為、数値がそのまま入っている部分が多くなっています。

 

エフェクトの実装は終了したので、後は実際に使うだけです。まず、エフェクトの初期化をします。第2引数に入れた数値は、サンプルでの画面の中心点です。

 

ShakeManager.SetupBlast(1.5f, new Vector2(400, 300));

 

次に更新処理を呼びます。

 

public override void Update(GameTime gameTime)
{
float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;
ShakeManager.Update(delta);
base.Update(gameTime);
}

 

そして描画時に実際に使用してみます。

 

</pre>
public override void Draw(GameTime gameTime)
{
GraphicsDevice.SetRenderTarget(0, m_renderTarget);
GraphicsDevice.Clear(Color.Black);

// ゲーム画面をテクスチャアに描画する
DrawContent(gameTime);

GraphicsDevice.SetRenderTarget(0, null);
spriteBatch.Begin();


// ゲーム画面を描画する
spriteBatch.Draw(m_renderTarget, Vector2.Zero, Color.White);

// エフェクトを描画する
ShakeManager.Draw(spriteBatch, m_renderTarget);
spriteBatch.End();
}

 

以上です。これで、最初に紹介した画像の様なエフェクトが描画されます。エフェクトの見かけは、振動の初期価値と、ShakeManager.ShakeStrengthの値を変更すると結構変わるので、試してみてください。

 

今回は、前回に引き続きHLSLを使って画面の色を反転する方法で紹介した処理を少しだけ変更し、違うエフェクトを作る方法を紹介します。

 

今回は前回実装したモノクロ処理に少しだけ処理を追加して、画面をセピア調に変更します。

 

まず、ピクセルの色を白黒からセピア調へ変更する際に使う色を.fxファイル内の上部に宣言します。

float4 sepiaTone = float4( 1.0f, 0.8f, 0.0f, 1.0f );  /ピクセルの調整値。

次に、ピクセルシェーダー内での処理で、前章同様白黒の色を計算し、その色に上記の色を掛けます。

float4 Sepia(PS_INPUT p) : COLOR0

{

// テクスチャアから現在の座標の色を獲得する

float4 col = tex2D( samplerState, p.TextureCoord.xy );

// 色を白黒化する

float tempPixel = ( col.r + col.g + col.b ) * 0.3333f;

//セピア調の色を計算する

col = tempPixel * sepiaTone;

return col;

}

これで後は、関数を呼ぶのを忘れないようにするだけです。

technique Flip

{

pass Pass0

{

PixelShader = compile ps_2_0 Sepia(); // Monotone, Negative()から変更

}

}

 

結構前のメモや、前回の物をそのまま使用した場合、これだけで動きます。

このメモでは、結構前のメモで紹介した色を反転させる処理を少しだけ変更し、画面の色を白黒に変える処理を紹介します。

 

このメモでの処理は、前回のコードを1行だけ変更すれば実装できるので、同じ説明でスペースと時間を無駄にしないため変更部分だけを解説します。なので、このメモを読む前に前のメモを読むことを強くオススメします。

 

では実装に入ります。前回の処理と違うのは、ピクセルシェーダー内で画面の色を変更する時の計算方法だけです。前回は[1 – colour.rgb]の式を使用して色を反転させましたが、今回はRGB値の平均を計算することで、白黒化した色を獲得できます。

 

.fxファイル内の、ピクセルシェーダーの関数は前章のから以下の用に変更されます。

float4 Monotone(PS_INPUT p) : COLOR0

{

// テクスチャアから現在の座標の色を獲得する

float4 col = tex2D( samplerState, p.TextureCoord.xy );

// 色を白黒化する

col.rgb = ( col.r + col.g + col.b ) * 0.3333f;

// 白黒の色を返す

return col;

}

これで後は、関数を呼ぶのを忘れないようにするだけです。

technique Flip

{

pass Pass0

{

PixelShader = compile ps_2_0 Monotone(); // Negative()から変更

}

}

 

 

今回は、前2回で作ったパーティクルシステムに、円形の様なエフェクトを追加します。

今回は実装方法として、一つのメソッド(Emit()メソッド)で別のエフェクトを作製します。なので、まずエフェクトの種類を表すenumを宣言します。

enum ParticleType
{
Linear, // 同じ速度のエフェクト
Spread, // ランダムな速度で移動
}

次に、Emit()メソッドに、ParticleTypeを渡せるようにします。その後に、ParticleTypeによって違う速度のパーティクルを作るようにメソッドの内容を変更します。

public void Emit(ParticleType type, Texture2D tex, Vector2 pos, int minAngle, int maxAngle, float scale,
float shrinkRate, float duration, int amount, int maxSpeed, Color colour)
{
Particle p;
float angle = 0.0f;
float speed = 0.0f;

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

if (type == ParticleType.Linear)
{
// 一定の速度を設定する。
speed = maxSpeed;
}

else if (type == ParticleType.Spread)
{
// ランダムな速度を獲得する
speed = m_rand.Next(1, maxSpeed);
}

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

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

後は、Emit()メソッドの呼び出しを変更するだけです。最初のエフェクトは、360度に向けて作成され、二つ目は150~230度に向けて作成されます。

protected override void Update(GameTime gameTime)
{
// スペースキーが押されたら
if (Input.IsPressed(Keys.Space))
{
// パーティクルを作る
emitter.Emit(ParticleType.Linear, particleTex, new Vector2(300, 300), 0, 360, 1.0f, 1.0f, 1.5f, 100, 200, Color.Gold);
}

if (Input.IsPressed(Keys.Z))
{
float scale = 0.5f;
float shrink = 0.5f;
int speed = 600;
int min = 150;
int max = 230;
Vector2 pos = new Vector2(350, 200);
emitter.Emit(ParticleType.Linear, particleTex, pos, min, max, scale, shrink, 1.0f, 40, speed, Color.Salmon);
emitter.Emit(ParticleType.Linear, particleTex, pos, min, max, scale, shrink, 2.5f, 40, speed, Color.Pink);
emitter.Emit(ParticleType.Linear, particleTex, pos, min, max, scale, shrink, 2.0f, 40, speed, Color.Crimson);
}

// パーティクルの更新
emitter.Update((float)gameTime.ElapsedGameTime.TotalSeconds);

base.Update(gameTime);
}

変更後はこんな感じになります。

以上です。気が向いたら別のエフェクトも追加してみますが、やるかどうかは今の所未定です。

今回は前回作ったパーティクルシステムを少しだけ変更して、作製するパーティクルの向きを限定します。

*注意: 前回の記事を書いた後に気が付きましたが、ParticleEmitterクラスのEmmit()メソッドは、本来Emit()という名前の筈なのに間違えてました。申し訳ありませんが、気になる方は修正してください。

では実装にもどります。まず、ParticleEmitterクラスのEmit()メソッドの引数に、角度の最低値と最大値を追加します。パーティクルの角度を獲得する時に、こに2つのパラメータを使用してランダムの角度を獲得するように変更します(実質パラメーターと一行を変更するだけです)


public void Emit(Texture2D tex, Vector2 pos, int minAngle, int maxAngle, 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(minAngle, maxAngle);

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

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

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

後は、Emit()を呼ぶときに少し変更するだけです。


// スペースキーが押されたら
if (Input.IsPressed(Keys.Space))
{
// パーティクルを作る
emitter.Emit(particleTex, new Vector2(300, 300), 30, 80, 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;
int min = 150;
int max = 230;
Vector2 pos = new Vector2(350, 200);
emitter.Emit(particleTex, pos, min, max, scale, shrink, 1.0f, 40, speed, Color.Salmon);
emitter.Emit(particleTex, pos, min, max, scale, shrink, 2.5f, 40, speed, Color.Pink);
emitter.Emit(particleTex, pos, min, max, scale, shrink, 2.0f, 40, speed, Color.Crimson);
}

変更後の動画:

以上です。何か今回は凄く短くなってしまいましたが、パーティクルシステム関係のメモはまだ最低1回続く予定です。