渋谷ほととぎす通信

エンジニア社長によるUnityとAIのブログ & エンジニアの生存戦略

【Unity】GetComponentは使わない方がよいのか?

【Unity】GetComponentは使わない方がよいのか?

こんにちは、Unityエンジニアのオオバです。

お悩みさん
お悩みさん
  • GetComponentって重いの?
  • GetComponentって使わないほうがよい?
  • transformでアクセスしないほうがよい?
  • オオバ
    オオバ
    本記事ではこれらの悩みを解決します。

    Unityはコンポーネントを開発してゲームを作っていくゲームエンジンです。プログラム上でコンポーネントのやり取りがよく発生します。

    お悩みさん
    お悩みさん
    コンポーネントの取得はGetComponentだっけ?

    その通りコンポーネントの取得には GetComponent を使用します。

    ところで 「GetComponentは重いから使わない方が良い」 と聞いたことがありませんか?とはいえコンポーネントを取得するために必要なGetComponentです。

    そこで本記事ではGetComponentが本当に重いのか、コンポーネントはどうやって取得したら良いのか解説していきます。コンポーネントとの扱い、付き合い方に困っている方はぜひ読んでみてください。

    コンポーネント自体はこちらの記事で詳しく解説しているので参考にどうぞ。

    👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

    GetComponentって何?

    「GetComponentってよく聞くけど何?」 という方に向けてGetComponentついて解説します。

    GetComponentはGameObjectにせとされているコンポーネントを取得する関数です。

    例えば以下のコードはGameObjectにセットされたRigidbodyコンポーネントを取得しています。

    Rigidbody rb = gameObject.GetComponent<Rigidbody>();  
    

    このようにGetComponentはGameObjectにセットされたコンポーネントを取得できる関数ということでよく使われます。

    GetComponentはGameObjectと同階層のコンポーネントしか取得できませんが、「GetComponentInChildren」「GetComponentInParent」を使用することで子階層、親階層のコンポーネントも取得できるようになります。

    しかしコンポーネントを動的に取得できて便利な一方、CPU負荷もかかってしまうのです。そこで本記事ではGetComponentとの付き合い方についてCPU負荷の観点から考察していきます。

    GetComponentは重いのか?

    結論から言うと重いです。Transformコンポーネントを例に検証してみます。

    • GetComponent<Transform>
    • .transform
    • 事前キャッシュ

    上記の通りTransformコンポーネントは3種類の取得方法があります。それぞれどのくらいCPU負荷がかかっているか計測してみましょう。

    【Unity】GetComponentは使わない方がよいのか?_0

    取得方法処理時間
    GetComponent約0.64ms
    .transform約0.54ms
    事前キャッシュ約0.09ms

    「GetComponent取得」と「.transformで取得」を比べると15.63%早く処理できています。

    そして「GetComponent取得」と「事前キャッシュ」を比べると85.94%処理が早くなりました(最速)。

    検証コードはこちら

    GetComponentが重いというより、もっと早くアクセスできる方法があるということを覚えておきましょう。

    GetComponentを使わずにコンポーネントを取得する方法

    コンポーネント取得は事前キャッシュが最速ということが分かりました。つまりGetComponentはあまり使わないほうが良いということです。

    実際に開発現場でGetComponentは使用しません。では事前にコンポーネントをどうやって取得するのでしょうか?

    コンポーネントを事前に取得(キャッシュ)する方法は2つあります。

    SerializeFieldを使用

    キャッシュしたいコンポーネントの変数に [SerializeField] をセットするとシーン、またはPrefabにコンポーネントをキャッシュできるようになります。

    具体的には次のようなコードです。

    using UnityEngine;  
    public class CacheComponent : MonoBehaviour  
    {
        [SerializeField] private BoxCollider _collider;  
    }
    

    上記ソースコードをGameObjectにセットしてみてください。

    【Unity】GetComponentは使わない方がよいのか?_1

    するとInspectorウィンドウにコンポーネントのフィールドが追加されます。ドラッグアンドドロップでコンポーネントのキャッシュができるのです。

    public変数を使用

    SerializeFieldと同様の機能をpublic変数にすることで実現できます。下記のコードはSerializeFieldののコードと機能的には全く同じです。

    using UnityEngine;  
    public class CacheComponent : MonoBehaviour  
    {
        public BoxCollider _collider;  
    }
    

    しかしpublic変数をコンポーネントのキャッシュに使用するのはオススメしません。なぜならpublic変数自体がプログラムを弱くするコードだからです。

    public変数はクラスの外からでも値を上書きできるため、思わぬところでトラブルが起きてしまう可能性があります。

    SerializeFieldとpublic変数どちらも利用可能なケースなら SerializeField を選択しましょう。

    GetComponentの使いどころ

    最後にGetComponentの使い所について解説していきます。

    結論 「動的にコンポーネントをつけ外しするケースで使用する」 です。

    どうしても事前にコンポーネントをセットできないケースが発生します。ランタイム中に「AddComponent」してコンポーネントをセットし、GetComponentで取得する。こういうケースでのみGetComponentを使用しましょう。

    事前にGameObjectにコンポーネントをセットできるケースであれば、前述したコンポーネントを事前キャッシュを使用する方がパフォーマンス観点からも良い状態を保てます。

    実際オオバもUnityを12年使ってきていますが、GetComponentを実際のプロダクトのコードで使ったことは数えるくらいしかありません。それくらい事前キャッシュしています。

    ということで GetComponentは使わざるを得ないときのみ使用 していきましょう。

    GetComponentは重いのか?まとめ

    本記事ではGetComponentとの付き合い方について解説してきました。

    簡単にまとめます。

    GetComponentは重いのか?まとめ

    ①コンポーネント取得の中でGetComponentは最も負荷が高い

    ②コンポーネントを取得の基本は事前キャッシュ

    ③GetComponentは使わざるを得ないときに使用

    GetComponentはとても便利なのでついつい使ってしまいがちですが、それなりに負荷が掛かってしまうということを覚えておきましょう。事前キャッシュすることで85%高速に処理できました。

    ゲームはシステムパフォーマンスによって体験が大きく変わってきます。パフォーマンスチューニングするタイミングで本記事のことも思い出してみてください(忘れないようにブックマーク!)

    コンポーネントの取得は基本的に事前キャッシュしてできる限りGetComponentを使わないようにしておくと最低限のコストで済みます。

    ぜひ参考にしてみてください。

    オススメ記事
    検証環境
    • Unity6000.0.32f1