【Unity】RigidBody2DにAddExplosionForceを追加する

RigidBody には AddExplosionForceというメソッドがあって爆発の表現を簡単に作ることができますが、RigidBody2D という2D向けのクラスには AddExplosionForce が無いため同じような処理を拡張メソッドで追加してみました。

追加した処理の実行結果はこんな感じです。

確認環境

  • Unity 2022.3.14f1
  • Windows11

動作はEditor上のみで確認しています。

実装コード

Rigidbody2DExtensions クラスを作成し Rigidbody2D の拡張メソッド AddExplosionForce を以下のように定義します。

// Rigidbody2DExtensions.cs

using UnityEngine;

public static class Rigidbody2DExtensions
{
    /// <summary>
    /// 指定したオブジェクトに爆発する力を加えます。
    /// </summary>
    /// <param name="self"></param>
    /// <param name="explosionForce">爆発の強さ</param>
    /// <param name="explosionPosition">爆発の中心点</param>
    /// <param name="explosionRadius">爆発の半径</param>
    /// <param name="upwardsModifier">追加の上向きの力</param>
    /// <param name="mode">ForceMode2D</param>
    public static void AddExplosionForce(this Rigidbody2D self, float explosionForce,
        in Vector2 explosionPosition, float explosionRadius, 
            float upwardsModifier = 0, ForceMode2D mode = ForceMode2D.Force)
    {
        Vector2 explosionDirection = self.position - explosionPosition;
        float explosionDistance  = explosionDirection.magnitude;

        if (upwardsModifier == 0f)
        {
            explosionDirection /= explosionDistance ;
        }
        else
        {
            explosionDirection.y += upwardsModifier;
            explosionDirection.Normalize();
        }
        
        Vector2 force = Mathf.Lerp(0, explosionForce, 
            1.0f - explosionDistance / explosionRadius) * explosionDirection;
        
        self.AddForce(force, mode);
    }
    public static void AddExplosionForce(this Rigidbody2D self, float explosionForce,
        in Vector3 explosionPosition, float explosionRadius, float upwardsModifier)
    {
        AddExplosionForce(self, explosionForce, 
            explosionPosition, explosionRadius, upwardsModifier, ForceMode2D.Force);
    }
    public static void AddExplosionForce(this Rigidbody2D self, float explosionForce,
        in Vector3 explosionPosition, float explosionRadius)
    {
        AddExplosionForce(self, explosionForce, explosionPosition, explosionRadius, 0);
    }
}

使い方

使い方は以下の通りです。

タッチした位置のワールド座標の半径 2.5ユニット以内の RigitBody2D を持つオブジェクトを列挙してそれぞれに AddExplorsion を指定します。

爆心地ほど力が強く外側に行くにつれて力が弱くなっていきます。与える数値はだいたい RigitBody の AddExplorsion と同じくらいの感覚で使えると思います。

// 与える力
[SerializeField] float _force = 3;
// 爆発半径
[SerializeField] float _radius = 2.5;
// 追加の上向きの力(今回は必要ないのでゼロ)
[SerializeField] float _upwardsModifier = 0;

readonly Collider2D[] _results = new Collider2D[100];
readonly ContactFilter2D _noFilter = new ContactFilter2D().NoFilter();

void Explosion(in Vector3 worldPos)
{
    _particle.transform.SetPos((Vector2)worldPos);
    _particle.Play();

    // 中心点から一定の距離のオブジェクトをすべて取得する
    int cnt = Physics2D.OverlapCircle(worldPos, _radius, _noFilter, _results);
    Log.Trace("cnt=" + cnt);

    // 各オブジェクトに爆発を設定
    for (int i = 0; i < cnt; i++)
    {
        var item = _results[i];
        var c = item.GetComponent<Rigidbody2D>();
        c.AddExplosionForce(_force, worldPos, _radius, 0, ForceMode2D.Impulse);
    }
}

参考

途中まで自分で考えてましたが検索したら普通に実装例があったのでこちらを参考にしています。

stackoverflow.com