はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】ColliderコンポーネントのアタッチされたゲームオブジェクトにはRigidbodyコンポーネントをつけるべきか検証してみた

はじめに

ツイッターをみていたところこんなツイートを見かけました。


確かにどこかでColliderコンポーネントのアタッチされたGameObjectにはRigidbodyコンポーネントをつけたほうが良いと見かけたような気がします。

これが本当かどうかを実際に実験してみたいと思います!

目次

環境

Unity2018.2.20f1

検証する対象

今回は5つの種類で検証をしました。
・RigidbodyなしかつTransform.position書き換え
・RigidbodyありかつTransform.position書き換え
・RigidbodyありかつIs KinematicオンかつTransform.position書き換え
・RIgidbodyありかつvelocity書き換え
・RigidbodyありかつAddForceで移動
それぞれの検証で細かい説明を付け加えながらいきたいと思います。

検証の条件

いくつか条件をつけて実験をしました。
・0.5秒に一回FPSの計測を20回行い、10秒間の平均FPSを計測する
・再生から5秒後に計測を開始する
・RigidbodyのDragとGravityはなしにする
・用いる3DモデルはSphire
・座標や初速といった初期条件は等しくする

FPSを計測するコード

平均FPSを計測するために以下のコードを用いました。
(誤りがあれば是非指摘していただけると嬉しいです)

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System;

public class MeasureFPS : MonoBehaviour {

    private List<float> fpsList = new List<float>();

    private int frameCount;
    private float prevTime;
    private int measureCount;
    private bool isMeasuring = false;

    private void Start()
    {
        //5秒経過後計測開始
        StartCoroutine(DelayMethod(5, () =>
        {
            frameCount = 0;
            prevTime = 0.0f;
            measureCount = 0;
            isMeasuring = true;
        }));
    }

    private void Update()
    {
        if (isMeasuring)
        {
            frameCount++;
            float time = Time.realtimeSinceStartup - prevTime;

            //20回計測したら平均を出力する
            if (measureCount == 20)
            {
                Debug.Log("avarage fps: " + fpsList.Average());
                isMeasuring = false;
            }
            else if (measureCount < 20)
            {
                //0.5秒間隔でFPSを計測する
                if (time >= 0.5)
                {
                    float fps_measure = frameCount / time;
                    fpsList.Add(fps_measure);
                    frameCount = 0;
                    measureCount++;
                    prevTime = Time.realtimeSinceStartup;
                }
            }
        }
    }

    private IEnumerator DelayMethod(float waitTime,Action action)
    {
        yield return new WaitForSeconds(waitTime);
        action();
    }

}

RigidbodyなしかつTransform.position書き換え

移動するコードは以下のとおりです。

using UnityEngine;

public class Player : MonoBehaviour {

    private Transform tr;

    private void Start()
    {
        tr = gameObject.transform;
    }

    private void Update()
    {
        tr.position += tr.forward * Time.deltaTime;
    }

}

f:id:hanaaaaaachiru:20190224004334p:plain

RigidbodyありかつTransform.position書き換え

移動コードは同様。

f:id:hanaaaaaachiru:20190224004119p:plain

RigidbodyありかつIs KinematicオンかつTransform.position書き換え

Is Kinematicとは

Is Kinematicは
有効にすると、オブジェクトは物理エンジンによって駆動されず、その Transform によってのみ操作されます。これは、プラットフォームを移動したい場合や、Hinge Joint を加えたリジッドボディをアニメーション化したい場合に便利です。
と公式に書いてあります。

これをオンにしたら変化があるかもしれないので調べてみます。

コード

移動コードは同様。

f:id:hanaaaaaachiru:20190224004412p:plain

RIgidbodyありかつvelocity書き換え

加えるvelocityの大きさは?

transform.position += transform.forward * Time.deltaTime;

と同じスピードにするにはどのくらいのvelocityにすればいいのかと計測してみたところ、

gameObject.GetComponent<Rigidbody>().velocity = new Vector3(0, 0, 1);

に匹敵するようです。

あまりに綺麗でびっくりしてしまいました。

コード

移動コードは以下のとおり。
追記)空気抵抗がないのでUpdateで更新する必要はありませんでしたね・・・

using UnityEngine;

public class Player : MonoBehaviour {

    private Rigidbody rb;

    private void Start()
    {
        rb = gameObject.GetComponent<Rigidbody>();
    }

    private void Update()
    {
        rb.velocity = new Vector3(0, 0, 1);
    }

}

f:id:hanaaaaaachiru:20190224005505p:plain

RigidbodyありかつAddForceで移動

どれくらいの力を加えればいいの

空気抵抗はない条件なので、最初にドンっとやってそのまま移動するのがいいですね。

ここはやや専門的なのでスキップして全然OKです。

f:id:hanaaaaaachiru:20190224012241p:plain

これもかなり綺麗にいけました!

コード

移動コードは以下の通り

using UnityEngine;

public class Player : MonoBehaviour
{
    private void Start()
    {
        gameObject.GetComponent<Rigidbody>().AddForce(new Vector3(0, 0, 1), ForceMode.Impulse);
    }
}

f:id:hanaaaaaachiru:20190224005505p:plain

結果

結果はこのようになりました。
①RigidbodyなしかつTransform.position書き換え
②RigidbodyありかつTransform.position書き換え
③RigidbodyありかつIs KinematicオンかつTransform.position書き換え
④RIgidbodyありかつvelocity書き換え
⑤RigidbodyありかつAddForceで移動

f:id:hanaaaaaachiru:20190224042547p:plain

f:id:hanaaaaaachiru:20190224042557p:plain

Rigidbodyの有無によってFPSの差は特に見られませんでした。

また、Rigidbody付きでTransform.positionの書き換えは他と比べてFPSが劣っているように見えます。

さいごに

正直なところ何度か検証すると平均FPS自体もばらつきがあり、頑張って中間あたりを記録しましたが正確とはいいがたいです。

ただこの結果からは
ColliderコンポーネントのアタッチされたGameObjectにはRigidbodyコンポーネントをつけるべきである
とは断言できません。

しかし公式には
「コライダーは Rigidbody コンポーネントをオブジェクトに追加することなくシーンにある床、壁など動かない要素を作成することができます。これらは 静的 コライダーと呼ばれます。一般的には静的コライダーのトランスフォームの位置の変更は物理エンジンのパフォーマンスに大きな影響をもたらすのでやめるべきです。」
と書かれています。


ぐぬぬ・・・。

おそらく検証の仕方が間違っていたのかもしれませんね・・・。

公式を信用しておいたほうが無難だとは思います。

ただ思ったより重労働だったのでもう当分はやりたくないですね笑