Unity5のコンポーネントキャッシュと僕の勘違い

ども、ゴコーです。

独学だと色々と勘違いすることもありますよねー。
今回は僕の勘違いを公開して皆さんに同じ轍を踏まないようにしてもらう、という主旨の記事になります。今回も中々長いので、飛ばしつつ読んでください。

この記事のテーマは

・GetComponent() について
・コンポーネントキャッシュについて
・プロパティを使う
・Unity5で消えた便利関数とその理由(と僕の勘違い)について
・じゃあ、いつでもコンポーネントキャッシュすればいいのか?

です。

GetComponent について

まずGetComponentについて知らないという人もいるかもしれませんので説明していきます。
知っている人は飛ばして頂いて構いません。

Unityではゲーム画面に映るオブジェクトも、映らないオブジェクトも色んなコンポーネントを組み合わせでできています。

参考過去記事:キューブ作成を通してゲームオブジェクトについて学ぼう【チュートリアル】

こうしたオブジェクトの中身を弄る/何かしてもらうために必要なのが、コンポーネントへのアクセスです。
まずは今回もキューブで簡単な例を見てみましょう。

※スクリーンショットの瞬間のFPSに注目しますが、当然多少の変動はありました。
環境にもよるので、FPSの値はあくまで参考程度にしてください。

例1:ただのキューブプレハブを900個インスタンス化。

compBatch

54.2FPSと、出すだけなら問題ないフレームレートを維持できています。

バッチングが効きまくってるんでしょうか。問題は次です。

例2:キューブを900個出し、Rigidbodyコンポーネント及び、Rigidbodyを用いたランダムな回転コンポーネントをアタッチ

回転は FixedUpdate() 内で行います。

compBatch2

オブジェクトが自分自身のRigidbodyにアクセスして回転しているだけなのですが、なんと一気に21.8FPSになってしまいました。わーお。

おまけ

しかし、Rigidbody は Unity5 で速度向上してると前に聞きました。そこでおまけ的に Unity4 でも同じスクリプトで試したところ、

compBatch3

絶望の 2.2FPS …。
間違いなく5になって速度は上がっていますね。凄い。

では、キューブにアタッチした回転用コードの中身を見てみましょう。

ランダムに角度を決定して AddTorque で回すだけです。

今回注目するのは GetComponent の部分。自分自身にアクセスするのでは無い場合は、さらに対象オブジェクトを探すところからスタートですが、今回は省きます。

僕の中の GetComponent &操作のイメージはこんな感じです。

①オブジェクトにアタッチされているコンポーネントをリスト化
②コンポーネント一つ一つが探してるコンポーネントか確かめる

get1

③探してるものならそれにアクセス。違えば次のコンポーネントへ
④指示を出す
get2

GetComponent() はちょっと重いと言われるのは、おそらくオブジェクト内の全てのコンポーネントにこうして聞きまくるからというのもあるでしょう。

しかしそれだけではありません。

2回目の GetComponent は

get3

こんな感じです。
コンポーネントの分だけ聞きまくるコンピューターくんは、誰が探してた人かをその場で忘れることが問題なのです。

コンポーネントキャッシュについて

上記のコードは「探して指示を出しては忘れ」を一秒間に何十回も繰り返しているのと同じです。

そして、コンポーネントキャッシュは言ってみれば「○○さんはどこにいた、どんな人かメモ」です。
メモしておけば、いざ操作したい時に「探す」手間がなくなるので速度が上がるのですね。

ではコンポーネントキャッシュする場合のコードです。

cRigidbody = GetComponent の行で、探しだした Rigidbody をメモしています。
後は何も変えていませんが…

compBatch1

24.3FPSに向上しました!
当然、こんな分かりやすく差がでるなんてことは中々無いことです。
注意点もありますが、それは最後にまとめてということで。

プロパティを使う

先ほどのコードは Awake() の中でコンポーネントをキャッシュしていましたが、より便利にするならプロパティを使いましょう!
プロパティがどんなものか説明してたら長くなるので省きますが、先ほどのコードは「Awake()でメモを取っている」という弱点があります。

どういうことかというと、900個オブジェクトがあれば、900個が、シーン再生開始直後に重い処理をするのです。当然多少固まります。
常に遅いのよりは断然ましですが、一瞬とはいえ処理落ちは処理落ち。プレーヤーさんも少なからずイラッとするでしょう。

Awake() だと起動の瞬間にしかメモできませんが、プロパティを使えば「必要になった瞬間にメモする」ということが可能です。今回は仕様上 Awake() で呼ぶのと変わりませんが、効果があるはずです!

コードはこんな感じです。

null 合体演算子便利ですよねー。

(2016/10/26追記)

上記のコードは元々null 合体演算子を使用して書いていました。この演算子は便利なのですが、Unityで使用すると事故を起こす原因になり得るので修正しました。

参考:Unityのnullはnullじゃないかもしれない

とりあえず「軽くなるかもしれない」程度の認識で良いですが、プロパティ自体が便利なので気になった人は是非勉強してくださいね。

Unity5で消えた便利関数とその理由(と僕の勘違い)について

今回、文中では意図的に GetComponent と書き続けてきましたが、Unity4 まで使ってきた人には違和感があるかもしれません。

なんで「rigidbody」使わないの?と。理由は簡単、Unity5で消えたからです。

知らない人のために書いておきますと、rigidbodyは Unity4.x まであった GetComponent() の言い換え表現です。
この便利関数について、過去に Unity ユーザー助け合い所で議論があったのですが、
「実際はソース見れないから分からんけど、計測すると便利関数から実行した方が速いからキャッシュしてんじゃね?」
という感じに落ち着いたのです。

それ以来僕は、便利関数使うとキャッシュされてるんだ!って思い込み、普通に便利関数を使っていました。しかし、Unity5ではこの関数が無くなるらしいと知り、リリースされたらつぶやこうかな、とか思ってました。

そしてこの記事の発端が、リリース後の僕のツイート。

そんなことをなんとなく書いたところ、数十秒後に

というツイートが頂けました。早すぎです大前さん。

曰く、元々内部ではキャッシュしてなかった気がする、とのこと。

話はそこで途切れたのですが、助け合い所での議論を深く覚えていた僕は、性懲りもなく次の日にまたツイートします。

全然まあ良さそうじゃない。未練たらたらじゃねーか、って自分でも思うようなツイートですが、それに対してまた反応が。

もうありがたすぎです。僕のような勘違いを減らすためだった、ということです。

うわー恥ずかしい。

皆さんも勘違いしないように!(お前が書くな)

じゃあ、いつでもコンポーネントキャッシュすればいいのか?

ここまで、コンポーネントキャッシュの利点を色々書いてきましたが、残念ながらいつでも通用するわけではありません。

まず、先ほどのように大量にオブジェクトを出した場合は明らかな差が出ましたが、逆に言えば「900個オブジェクトを出して比べたらやっと分かる」程度の効能なのです。そこまで万能なものと期待してはいけません。

次に、コンポーネントキャッシュをする(メモを取る)には覚えておくためにメモリを消費する必要があります。
大量にキャッシュを取れば取るほどメモリを消費をするわけですから、それはそれで遅くなる原因になるかもしれませんし、覚えておいたキャッシュを捨てるのも捨てすぎるとそれはそれで遅さの原因になります(C#では特に)。

長く生存するオブジェクトに継続的にアクセスするならキャッシュすれば良いかもしれない、というところでしょう。

場合にもよるので自分で必要な場面を考えて使えるのが理想でしょう。僕もそうなりたいです。

では、また。

Comments

comments

スポンサーリンク
336*280px

コメント

  1. @shoji_hibino より:

    作法変わってる。//Unity5のコンポーネントキャッシュと僕の勘違い http://t.co/i685U9tmvn

  2. @tkch_str より:

    Unity5のコンポーネントキャッシュと僕の勘違い http://t.co/rUHBmfKrzI

    なるほど…

  3. @Singulith より:

    Updateの中でGetComponentを回しても、キャッシュするのとそんなには速度は変わらないんだ・・
    なんかのバグが回避されるとかないかな?

     >Unity5のコンポーネントキャッシュと僕の勘違い

     http://t.co/UcbaOvUcew

  4. @ichiiyoichi より:

    ああ、まさにGetComponentについて見えてきたイメージをそのまま解説してくれてる。
    /Unity5のコンポーネントキャッシュと僕の勘違い
    http://t.co/VhavcH5W4v

  5. @ichiiyoichi より:

    というかupdateで動きまくる処理にこの処理書かないほうがいいのか。

    http://t.co/VhavcH5W4v

  6. @negipote より:

    ググった方法じゃRigid body 取れないぞと思ったらこういうことかー
    Unity5のコンポーネントキャッシュと僕の勘違い http://t.co/AgBHk5qnT2