うにty生活

UnityChanを生活させたい

Rigidbodyを使わずにオブジェクトを反発させる表現方法

はじめに

今回はRigidbodyを使わずにSpriteが重ならないようにする方法を紹介します。

 

Spriteが重ならないようにしたい場面は頻繁にあるのですが、重ねないためだけにRigidbodyを使うとパフォーマンス的に心配ということもあり

今回はTransform移動だけでRigidbodyを使った時のようなSprite同士が反発しあう表現を行っていきます。

 

今回の記事ではTransform移動だけでこんな感じの反発表現ができるようになります。

 

表現の近いゲーム

・ヴァンパイアサバイバーの敵

・Stocklandsのカード

 

準備

検証するための簡単にステージを用意しました。

サンプルステージ画像

 

貝(敵)のモンスター画像は以下のサイトから拝借

七三ゆきのアトリエ
https://nanamiyuki.com/

 

今回はこの貝を敵として大量描画を行い、画面外から中央に向かって移動させます。画面中央に設置した判定に当たると敵は消えるようにしています。

※敵の出現方法のコードは割愛します。

RigidBodyを使った場合

まずはRigidBodyを使ってどんなことがしたいのか見せます。

Spriteを重ならないようにする方法で検索すると大体このRigidBodyのDynamicを利用しています。(そういう紹介をしている人がほとんど)

※ただRigidbodyを使うには物理挙動を理解していないといけないし、FixedUpdateが必須となってくるので導入難易度は少し高いです。

 

TargetMove.cs

public class TargetMove : MonoBehaviour
{
    public Vector3 targetPosition;
    private Rigidbody2D rigidbody2d;

    // Start is called before the first frame update
    void Start()
    {
        rigidbody2d = GetComponent();
    }

    private void FixedUpdate()
    {
        rigidbody2d.AddForce((targetPosition - transform.position).normalized * speed);
        rigidbody2d.velocity = new Vector3(rigidbody2d.velocity.x, rigidbody2d.velocity.y) / 1.1f;
    }
}

 

このスクリプト時の貝(敵)のコンポーネントはこんな設定です。

貝のコンポーネント

貝(Enemy)のコンポーネント(Rigidbodyあり)

 

プレイしてみる

 

これをTransform移動で表現していきます。

 

RigidBodyを使わない場合

TargetMoveスクリプトは以下です。

public class TargetMove : MonoBehaviour
{
    public Vector3 targetPosition;
    public float speed;
    public float repulsionCoefficient; //反発係数
    public Vector2 origin; //原点調整用
    public float radius; //検知半径
    public LayerMask layerMask;

    // Update is called once per frame
    void Update()
    {
        //通常移動
        var directionVector = (targetPosition - transform.position).normalized;
        transform.position += directionVector * Time.deltaTime * speed;
        
        //反発移動
        var collision = CheckOverlapCircle();
        if (collision != null)
        {
            Vector3 repulsionVector = (collision.gameObject.transform.position - transform.position) * Time.deltaTime * repulsionCoefficient;
            transform.position -= repulsionVector;
        }
    }

    private Collider2D CheckOverlapCircle()
    {
        var hit = Physics2D.OverlapCircle(transform.position + (Vector3)origin, radius, layerMask);
        return hit;
    }
}

 

解説

毎フレーム自身と同じobjectが重なっているかどうかチェックして、重なっていればそのobjectと反対方向に速さが加わります。

Physics2Dは物理挙動を一部利用、設定できるクラスです。

Unity - Scripting API: Physics2D

 

このコードで問題点があるとすれば進行方向と逆側で重なってしまうと少しスピードが上がります。

 

Inspectorの作業としては

・レイヤーを作成してEnemyObject本体とTargetMoveスクリプトに設定

・各種パラメーターを設定します

・Rigidbodyは必要ありません

貝(Enemy)のコンポーネント2

貝(Enemy)のコンポーネント(物理挙動なし)

 

この状態で実行するとSpriteが重ならず、お互いに反発しているように見えます!

(最初の動画と同じ)

 

 

パフォーマンスについて

どちらも似たような表現ができますが、大切なのはどちらがパフォーマンスがよいかということです。今回はシンプルに敵の数を増やして検証します!

 

結果から言うとパフォーマンス的にはほぼ同じに見て取れます。

ですのでFixedUpdateを使うことができないなど、ケースによって使い分ければよいかなと思います。

 

検証方法はProfilerで確認していきます。

 

Profiler(Rigidbodyなし)

Profiler(物理挙動なし)

 

Profiler(Rigidbodyあり)

Profiler(物理挙動あり)



検証結果

結局Transform移動といってもPhysics2Dは使っているので、双方FPSはそんなに差はなかったです。RigidbodyありのほうがPhysics(ピンク)の処理が大きいということはやっぱり反発以外の処理も行っているという予想は当たってました。

 

あと当たり前かもしれないですが、双方オブジェクト数が増えすぎるとFPSは下がるので、今回のセンターに集まったら消える処理のような全体数を一定にコントロールするようなコードがあってもいいと思います。

 

久々に技術系書いた、終わりー