IPFactory Advent Calender 2023 11日目の記事です。
はじめに
Unity2Dを用いた爆発の実装方法をまとめた記事が見当たらなかったので、かみ砕いて解説してみました。
ぜひお手元で実装してみてください!
目次
用意するもの
1.爆弾の画像
こちらの画像を使わせていただきました。
2.エフェクト
今回はこちらの動画をもとにParticleSystem
を用いて自作しました。
非常に分かりやすい動画ですので是非参考にしてみてください。
自作しない場合はAssetStoreから持ってくるか、用意できない場合は無くても大丈夫です。
下準備
1.ステージ
舞台となるステージを用意します。
Hierarchy ビューで右クリック → 2D Object → Sprite から好きな形を選びScene上に配置してください。
配置できましたら当たり判定をつけるため Inspector ビューでAdd Componet → Physics2D → Boc Collider 2D を選びステージの大きさに合うように調整してください。
2.箱
爆発の対象となる箱を用意します。
ステージと同じように、Scene上にbox配置し、コライダーをアタッチしてください。
また、箱には重力を適用したいため Inspector ビューでAdd Componet → Physics2D → Rigidbody 2D を選んでください。
ここでゲームを開始すると箱がステージの上に落ちるかと思います。
3.爆弾
爆弾ですが、基本的には箱と同じですのでboxをコピーしてbombに名前を変更してください。
bombは分かりやすいよう見た目を変えます。用意した爆弾の画像を Inspector ビューのSprite Render → Sprite にアタッチしてください。
爆弾は丸いのにboxをそのまま使っているためコライダーが BoxCollider2D になっています。
BoxCollider2Dを削除後、Inspector ビューでAdd Componet → Physics2D → Circle Collider 2D をアタッチし、画像に合わせて形を変更してください。
これで下準備は完了です。
箱は多いほど爆発の影響を感じやすいのでお好きな数配置してください。
爆発スクリプトの作成
Assetsで右クリック → Create → C# Script でスクリプトを作成し、名前をBombに変更してください。
スクリプトをダブルクリックで開き、以下のコードを張り付けてください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bomb : MonoBehaviour
{
[SerializeField] private ParticleSystem explosionParticleSystemPrefab; // 爆発時のパーティクルシステム
[SerializeField] private float explosionForce; // 爆発力
[SerializeField] private float explosionRadius; // 爆発半径
private void OnMouseDown()
{
Detonate();
}
// 爆発処理
void Detonate()
{
// パーティクルシステムを生成して爆発エフェクトを再生
ParticleSystem explosionParticleSystem = Instantiate(explosionParticleSystemPrefab, transform.position, Quaternion.identity);
explosionParticleSystem.Play();
// パーティクル再生時間が終了したらパーティクルシステムを破棄
Destroy(explosionParticleSystem.gameObject, explosionParticleSystem.main.duration);
// 爆風の範囲内のオブジェクトを検出
Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, explosionRadius);
foreach (Collider2D collider in colliders)
{
ApplyExplosionForce(collider);
}
// 爆弾オブジェクトを破棄
Destroy(gameObject);
}
// 吹き飛ばしの処理
void ApplyExplosionForce(Collider2D targetCollider)
{
Rigidbody2D targetRigidbody = targetCollider.GetComponent<Rigidbody2D>();
if (targetRigidbody != null)
{
// 爆心からの距離に応じて力を計算
Vector2 explosionDirection = targetCollider.transform.position - transform.position;
float distance = explosionDirection.magnitude;
float normalizedDistance = distance / explosionRadius;
float force = Mathf.Lerp(explosionForce, 0f, normalizedDistance);
// 力を加える
targetRigidbody.AddForce(explosionDirection.normalized * force, ForceMode2D.Impulse);
}
}
}
コードの解説
上記のコードをかみ砕いて解説していきます。
コピペで動きますので実装だけしたい方は飛ばしてください。skip→
1.変数定義
[SerializeField] private ParticleSystem explosionParticleSystemPrefab; // 爆発時のパーティクルシステム
[SerializeField] private float explosionForce; // 爆発力
[SerializeField] private float explosionRadius; // 爆発半径
変数は上から順番に
- 爆発エフェクト
- 爆発力
- 爆発半径
になります。爆発力・爆発半径ともに9
を代入しているという前提で進めていこうと思います。
※ 変数はスクリプト上で値を代入せず、後で Inspector 上で変更していきます。
2.クリックで爆発
private void OnMouseDown()
{
Detonate();
}
OnMouseDown
メソッドはオブジェクトがクリックされたときに呼び出されます。
要するにクリックされたときに爆発するようになっています。
3エフェクトの生成
void Detonate()
{
// パーティクルシステムを生成して爆発エフェクトを再生
ParticleSystem explosionParticleSystem = Instantiate(explosionParticleSystemPrefab, transform.position, Quaternion.identity);
explosionParticleSystem.Play();
// パーティクル再生時間が終了したらパーティクルシステムを破棄
Destroy(explosionParticleSystem.gameObject, explosionParticleSystem.main.duration);
// ----- 省略 -----
}
explosionParticleSystem
変数にtransform.position
(爆弾の位置)に生成したexplosionParticleSystemPrefab
(爆発のエフェクト)を代入しています。
生成したエフェクトexplosionParticleSystem
を再生し、再生が終わり次第破棄するという流れになっています。
※ エフェクト(パーティクル)が用意できなかった方は上のコードは消してください。
4.爆発範囲にいるオブジェクトの取得
void Detonate()
{
// ----- 省略 -----
// 爆風の範囲内のオブジェクトを検出
Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, explosionRadius);
foreach (Collider2D collider in colliders)
{
ApplyExplosionForce(collider);
}
// 爆弾オブジェクトを破棄
Destroy(gameObject);
}
Physics2D.OverlapCircleAll
で爆弾を中心とした半径explosionRadius
の当たり判定を生成し、範囲内のオブジェクト全てがApplyExplosionForce
(爆風を加える)メソッドを実行させています。
爆弾は爆発によってなくなりますのでDestroy(gameObject);
で破棄します。
5.Rigidbody2Dの取得
// 吹き飛ばしの処理
void ApplyExplosionForce(Collider2D targetCollider)
{
Rigidbody2D targetRigidbody = targetCollider.GetComponent<Rigidbody2D>();
if (targetRigidbody != null)
{
// ----- 省略 -----
}
}
targetRigidbody
に取得したオブジェクトのRigidbody2Dを取得し、代入します。
このRigidbody2Dは後で爆風を与える時に使います。
基本的にはtargetRigidbody
にRigidbody2Dが代入されているはずです。
しかしステージなどのコライダーを持っていながらRigidbody2Dを持っていないオブジェクトによりエラーが起きる可能性があるためif (targetRigidbody != null)
でnull検知を忘れないでください。
6.オブジェクト間のベクトルと距離を求める
// 爆心からの距離に応じて力を計算
Vector2 explosionDirection = targetCollider.transform.position - transform.position;
float distance = explosionDirection.magnitude;
boxが受ける爆発力を計算していきます。
ここではbombの座標を(1, 2)boxの座標を(8, 5)だと仮定して進めていきます。
最初に、爆弾から箱へのベクトルを
Vector2 explosionDirection = targetCollider.transform.position - transform.position;
で計算します。
boxの座標 - bombの座標 → (8, 5) - (1, 2) = (7, 3)
でオブジェクト間のベクトルが(7, 3)
と求まりました。
ベクトルが求まりオブジェクト間の角度が分かるようになりましたが、肝心なオブジェクト間の距離が分かりません。
そこで先ほど求めたベクトルを用いてオブジェクト間の距離を
float distance = explosionDirection.magnitude;
で計算します。
magnitude
は、与えられたベクトルから自動的に距離に変換してくれるプロパティのため、7.615773
と距離が求まりました。
\text{magnitude} = \sqrt{x^2 + y^2}
7.爆風を計算し力を加える
float normalizedDistance = distance / explosionRadius;
float force = Mathf.Lerp(explosionForce, 0f, normalizedDistance);
// 力を加える
targetRigidbody.AddForce(explosionDirection.normalized * force, ForceMode2D.Impulse);
最初に先ほど求めたdistance
(距離)を正規化していきます。
ここでの正規化とはdistance
がexplosionRadius
(爆発半径)の何倍になるかを指します。
float normalizedDistance = distance / explosionRadius;
先ほど求めた距離7.615773
から爆発半径である9
で割ると正規化された距離である0.8461975
が求まりました。
これにより、半径9
を1
としたときにboxは0.8461975
の位置にいるということが分かりました。
次にboxが受ける爆発力を求めます。
爆発力explosionForce
は9
と定義したので爆発力と位置の関係を表すと以下のようになります。
爆発の威力は爆発の中心が一番が高いので、爆発力9
になります。
また、爆発半径は9
なので、爆発を中心とした半径9
を超えると爆発力が0
になることが分かります。
この関係から
float force = Mathf.Lerp(explosionForce, 0f, normalizedDistance);
を用いて最大値9
を始点、最小値0
を終点として爆発の中心からの距離である0.8461975
で線形補完することで、box位置の爆発力である1.384223
という値が求まりました!
最後に、対象オブジェクトのRigidbody2D
に今求めた力を加えます。
targetRigidbody.AddForce(explosionDirection.normalized * force, ForceMode2D.Impulse);
加える力はForceMode2D.Impulse
にすることで爆発のような瞬間的な力がかかるようになります。
力を加える角度は最初に求めたオブジェクト間のベクトル(bombからboxの角度)に設定し、その角度にいま求めたfornce
(box位置の爆発力)を加えると...
/
ドカーーーン!!
\
飛びました!!
爆弾の仕上げ
完成したBombスクリプトをBombにアタッチしていきます。
Hierarchy ビューからBombを選択 → Inspector ビューにBombスクリプトをドラッグアンドドロップ
変数にはそれぞれ
- 用意したエフェクトをアタッチ
- 爆発力
9
を入力 - 爆発半径
9
を入力
エフェクトがない場合は空でも大丈夫ですが、こちらの3行を消しておいてください。
xplosion Force
・Explosion Radius
には色々な値を入れて動きの違いを確かめてみてください。
動かす
完成です!
爆弾をクリックしてみると...
吹き飛びました!!
さいごに
できるだけかみ砕いて解説してみました。
ゲーム制作の参考になれば嬉しいです!