UIのリビルドについて

山村達彦氏(以下、山村):では、フィルレートの話も終わったので、次の話をしましょう。UIのリビルドについてです。これが初耳という方は、どれぐらいいらっしゃいますか?

(会場挙手)

意外と少ない。じゃあきっと大丈夫ですね。UIのリビルドの話をしましょう。これがUnityのUIにおいて大事な部分です。ぶっちゃけ前の2つは忘れても良いので、これだけは覚えてもらえればと思います。

(会場笑)

先ほども紹介しましたように、UnityのCanvasは、描画をまとめてバッファを作って、それを使って描画します。UnityのUIの流れはこれです。

Canvasになにかをやらせる。Canvasで描画をまとめて、描画する。

このバッファはCanvasで1回作るんですが、これは基本的に使い回されます。これのなにが良いかというと、UIを大量に置いた場合でもほとんど負荷がかかりません。これはiPad Proで検証したもので、がんばって2,300個置いてみました。

周りにもっと大量に置いた場合でも、そこまで負荷になりませんでした。基本的にはバッファを使いまわして描画するので、描画の効率がすごく良いです。ほとんど負荷がかかりません。

ただ、バッファを使い回すということは、同じデータしか使えないことを意味しています。もしそのバッファの情報に変化があった場合、Canvasに情報をまとめて1回統合して、バッファを作り直す必要があります。

これがUIのリビルドです。基本的に、なにか変化があるたびに、UIはリビルドする必要があります。

といっても、1フレームの中で何十回も更新されるtransformみたいなシステムではなくて、SetDirtyで汚れているものをいったんまとめて、そのあとにLateUpdateのタイミングでバッファを再構築して作り直すということをやっています。

基本的に、UIを更新したときにその更新したUIがDirtyに入って、そのDirtyが入っている情報をもとに更新していく感じです。

更新される条件はいくつかあります。

例えば、UIをenable/disableした場合。消したら当然非表示になるので、バッファは消えます。マテリアル、バッチングが切り替えられた場合にも更新されます。あとはUIのRectTransform。つまり、UIの大きさが変わってしまった場合でも更新されますし、transformの親が変わった場合も更新されます。

1つの変更ですべてに影響が出てしまう

ここで、UIのリビルドが問題になります。なにが問題かというと、1つのUIが変わるとすべてが変わってしまうということです。

例えば、1個に対してSetDirtyを行ったとします。1個のUIを変えたとしても、生成しているのはCanvasです。Canvasは1回すべてを集めて作り直しています。なので、変更対象はすべてになってしまいます。1個のUIを更新した場合、1個のImageを差し替えた場合、1個のTextを書き換えた場合でも、すべて変更対象になります。

例えば、ここではちょっと色の違うアーチャーを置いて、その背景に2,300体のキャラクターを置いています。ここで手前にいるキャラクターにアニメーションを再生させると、こんな感じですね……。(キャラクターがバタバタ切り替わる)

これをやると、(動いているのは手前のキャラクターのみだが)背景のキャラクター全員分だけ、バッファを作り直すことになってしまいます。手前のキャラクターが動いているだけなのに、それ以外のキャラクターは全部作り直しです。

そうなると、先ほどはぜんぜん負荷がなかったにもかかわらず、これをやった瞬間に「Render」のところに長いバーが出現しています。

ただ、幸いなことに、「メインスレッド」には実はそれほど負荷がありません。今までこういった処理は全部メインスレッドでやっていましたが、Unity5.2あたりでこれは全部別スレッドに移行して、ほとんどの処理をメインスレッドから取り除いています。なので、メインスレッドに対してはそれほどインパクトはありません。

ただし、ジョブの1個でCanvasのバッファを作り直しているんですが、これがとんでもなく長いということが起こっています。実際、レンダースレッドの長さはほとんどジオメトリのバッファの作り直しの終了待ちなんですが、こういうことが起こってしまいます。

また、先ほどテクスチャをちょっと離した場合、バッチングを効率的にやってくれるようにソートしてくれるとお話ししましたが、1箇所にみっちりとUIが詰まっている場合、このソーティングもちょっと伸びるかもしれません。

この現象はどうやって確認すれば良いのか? なにかスレッドが伸びているときには、その要因を確認する必要があります。

どうすれば良いのかというと、またUI Profilerが登場します。

正確にはProfilerのUIなんですが、Profilerでなにかリビルドが走っている場合は、すごくいっぱい山が起きます。左側の画面ですね。

こうなっている場合はリビルドがみっちり走っているので、できるだけ避けるほうが良いです。右側はなにも起こらなかった場合ですね。なにも起こらなかったら、すごく穏やかな波になっています。

動いている場合は毎フレームレイアウトを作り直しているので、CPUをパッと見た感じだとぜんぜん負荷になっていないように見えますがフレームが落ちているとか、そういうふうになります。

もう1個の確認方法としては、(スライドを指して)先ほど紹介したこれです。

すごく特徴的な画面なのでわかりやすいですが、紫色のものがみょーんと伸びていたらやばいです。もしくは、ワーカースレッドでジオメトリの作り直しをやっている場合は、「たぶんUIのリビルドが走っているな」ぐらいに思ってもらって大丈夫です

UIリビルドの原因を特定する

UIのリビルドが起こっているのは問題ありません。誰が起こしたのかが重要です。これを確認する方法を紹介しましょう。

まず、CPU Profilerで、「OnDidApplyAnimationProperties」とか「OnRect〜」と検索します。これを検索すると、このUIをリビルドしているものが発行している命令を見つけることができます。つまり、この命令を発行しているものがリビルドを起こしています。

CPU Profilerでは「Show Related Objects」を使うことによって、どのオブジェクトが命令を発行しているのかを確認することができます。今回の場合は12番目のImageがなにか問題を起こして、なにか発行していることがわかります。

これをなんとかしようという話なんですが、当然のごとくUIは動くものなので、ずっと止まっているUIはあまりありません。なのでどうやれば良いのかというと、一番手っ取り早いのはCanvasの分割ですね。

Canvasは基本的に一番上にあって、その下にUIを置いてという話になっていますが、そのUIの真ん中あたりにCanvasをたくさん置くことができます。SubCanvasと呼ばれる技術です。

このCanvasを複数用意することによって、トップのCanvasとは独立したCanvasを描画することができます。これはOrder in Layerであったり描画順であったりして、完全に独立して表現することができます。

これによって描画が独立するので、影響範囲を減らすことができます。これはDynamicに動くCanvas・Staticと、絶対に動かさないCanvasとを分けています。先ほど紹介したCanvasの中でどれかが1個動くんですが、それを動かしてしまった場合全部動いた判定になってしまって、すべてのUIをリビルドするという話になってしまうのでよくありません。

なので、動かすものはDynamic Canvasに移動、もしくはCanvasを設定しておいて、それに対して動かします。そうすることで、Dynamic Canvasは普通にDirtyされてDynamic Canvas単位でリビルドされ、StaticなCanvasは、別のCanvasなので気にする必要がなくなります。

動かすCanvasは動かすCanvasのところに置いて、もしくはそのUIにCanvasをくっつけてしまって、動かないCanvasは一気にまとめておけば良い。Pixel Perfectが使えるので、すごく効率的です。

先ほども紹介したとおり、場合によってはCanvas単位で描画できるようになるんですが、逆にいえばこのCanvasの分割はバッチングが絶対に効かないので、描画のドローコールが増えていくリスクはあります。つまり、キャラクター、UIを全部Canvasにすると、動かなかったらめちゃめちゃ効率が良い描画というのものができなくなります。それだけは注意する必要があります。

今回は手前で動くキャラクターにCanvasをくっつけましたが、こんな感じでほとんど負荷がなくなりました。基本的に、手前で「イエーイ!」と叫んでいるものだけがリビルドされます。

動くUIはImageのSprite Meshを避ける

もう1個あります。UIのリビルド、つまりポリゴンを結合して一気に描画できるわけですが、基本的にUIはQuadでした。でも、Quadでなくてもできるようになりました 。つまり、Spriteできるようになったんですが、このSpriteは、バッチングにとても時間がかかります。

したがって、今までは6つだったポリゴン情報が、「64〜256」といった単位になるので、とくにポリゴンが多い場合はこれをバッチングするのは非常にコストがかかります。少ない場合は大した問題ではありません。

use sprite meshはフィルレートも減らせるしバッチングにも便利なんですが、バッチングをする観点から見ると、コストが高くなることがあるかもしれません。

これは実際にuse sprite meshを有効にした場合と、完全に無効にした場合のバッファの生成時間です。Sprite Meshを普通に使った場合は3.2msかかったんですが、これがQuad、つまり四角形だと0.4msで一瞬で終わる。

なので、場合によってはどちらかを選択する必要があります。フィルレートを取るか、バッチングを取るか。ダウンロードサイズを取るか、CPUの処理力を取るか。そういう話ですね。

UIのバッチングに関して、もう1個だけ話すことがあります。

親を動かすとバッファの作り直しが発生するという問題です。例えば、RectTransformの下にImageを大量に置いて、親のRectTransform を動かすと、ガクガクすることがあります。

これは、UI Profilerで確認できます。親のオブジェクトを動かすと、Renderのほうのバーが動いている瞬間だけグイッと伸びます。なにをやったにせよ、親を動かすとバッファの作り直しが起こってしまいます。

残念ながらこれには対策はありません。要素数を減らすとか、ListViewみたいなものを作った場合、要素が一番下に来たら上に持っていくみたいな、データドリブンなやり方を実装する必要があります。

残念ながら、こういったものを実装しないと、大量のUIを用意してスクロールしたら止まってしまうことがよくあります。

同時に、画面にあるように、これは「Screen Space - Camera」といって、カメラの前面にCanvasを置く機能です。「ScreenSpace - Overlay」のようにレイアウトを維持したまま描画できる機能ですが、カメラを動かすと残念ながらUIが再構築になります。

なので、もしこのUIを「Screen Space - Camera」でやりたい場合は、最小限のUIで構築するように考える必要があります。もしくは、いったんテクスチャに焼いてしまって表現するとかですね。それはイベントカメラを別に持っておけば、良いアイデアかもしれません。

Layout Groupについて

次はLayout Groupです。「Layout Groupとはなんぞや?」という話についてです。Vertical LayoutやHorizontal Layoutなどといったものですね。機能が複雑すぎてわからない人がけっこう多いのではないかと思います。自分もちょっと前までわからなかったところもありましたし、あまり評判もよくありません。こちらもリビルドを持ちます。このLayout GroupのUIのリビルドについてお話ししようと思います。

その前にLayout Groupをちょっとおさらいしましょう。Layout Group、例えばRectTransformの中にUIがいっぱいあった場合、これを「ILayout Group」を継承したコンポーネントが、整列させます。これは「ILayout Group」が下のコンポーネントを見て整列させてくれます。

同時に、「ILayoutSelfController」というコンポーネントが自分自身を変形させます。例えば、UIのサイズに合ったサイズに調節したり、Content Size Fitterしたりといったことです。

この機能を使うと、もしUI要素を足したりUIの大きさがちょっと変わったりした場合でも、ちゃんと追随して表現できます。

うまく組み合わせると、例えばこれはRectTransformなんですが、ここに4つ並べて、その下に要素の異なるUIをポンと置いて、でも破綻しないレイアウトで作るということが実現できます。

これは非常に柔軟性が高くて良い機能なんじゃないかと思ったんですが、やはりこいつはいくつかまずい問題を持っています。

Dirty Systemのしくみ

その前にDirty Systemの話をしましょう。

UIは基本的にDirty Systemを採用しています。Dirty Systemとはなにかというと、汚れたら変更されたと認識して、それをもとにUIに反映させるという機能です。例えばファイルをDirtyして、あとで全部セーブするといったことをやっていることが多いです。

UIシステムもこれと同じで、例えばこれはLayout Groupの下にElementが配置されている状態です。このうちの1個のサイズを変えます。ちょっとサイズを伸ばしてみました。サイズを変更したので、これでDirtyに入ります。Dirtyが入ったので、Layout Groupにリビルドを申請しまして、あとですべてのレイアウトがちゃんと並ぶようにLayout Groupがレイアウトしてくれます。

もしそのLayout Groupの上にLayout Groupがあった場合、レイアウトは、ネスト……階層ができるので、一番下のLayout GroupのElementの大きさが変わった場合は、親のLayout Groupに話して、その一番上のLayout Groupから順番にちゃんとレイアウトを調整してくれます。

これは非常にありがたい機能です。

なので、そのLayout Groupの間の中に、Content Size Fitterなどのレイアウトのサイズが変わってしまう系のものを突っ込むと、非常に具合が悪くなってしまうのですが、それは置いておいて、問題の話をしましょう。

DirtyするUIが紛れ込むと、毎フレームUIのリビルドが走る

先ほど、「親に通知して、その上の親に通知して」という話はしました。これはすごくインテリジェントな方法だと思います。これをどうやっているのかが問題です。

これは、GetComponentしています。parent.GetComponentで親のUIが持っているコンポーネントを全部取ってきて、その中でLayout Groupのものを見つけて、そこに渡します。なので、通知確認はGetComponentをたくさん取ります。複数あった場合は、それだけ負荷がかかります。

それを一番上のrootのLayout Groupまで繰り返します。つまり、一番下のElementをGetComponentから取ってきて、そのLayout Groupがまた親のLayout GroupにGetComponentします。

UIの自動レイアウトシステムは、こういった柔軟なところが良いと思うんですが、こうしたダイナミックな重い負荷をガンガン突っ込むところは癖がありまして、パフォーマンスに問題が出やすいので、そこがあまりイケてないというのはそのとおりだと思います。

当然、戻る方法もGetComponentです。Layout Groupが整列するためには、この情報を持ってくる必要があるので、それをGetComponentで持ってきてレイアウトします。なので、Layout Groupの中にこの要素がたくさんあった場合、たくさんのGetComponentが走ります。

GetComponentは、Unityに触っている人はあまりやりたくないと思いますが、このような仕組みになっています。

ただ、これは致命的な問題ではありません。一番致命的な問題は、DirtyするUIが1個でも紛れていた場合、毎フレームすべてのUIのリビルドが走ってしまう点です。

先ほどお話ししたように、1個のUIがあったら親にリビルドを申請して、その親にリビルドを申請して、戻っていく。つまり、このUIの中に1個だけ裏切りものがいるんですが、わかりやすく絵が違います。裏切り者がいたとして、1個でもUIをリビルドする要素をやっていた場合、すべてのUIはリビルドが走ってしまいます。

なので、このUIの要素があると問題になります。しかも、GetComponentで使うUIのリビルドです。やはり間違いなく問題になります。というか、これが一番問題です。

ちなみにどのぐらい負荷が高いかというと、先ほどの2,300体キャラクターを配置する例でリビルドすると、UIの再構築だけに、このパソコンで12secかかりました。

UI Profilerでもしこれが起こっていた場合は確認することができます。UI Profilerがガンガン伸びていたら、やっぱりUIの再構築が起こっている。これも同じですね。

残念ながら、このUIのレイアウトのリビルドはメインスレッドで動きます。なので、メインスレッドにおいてレイアウトでがっつり長いものをやっていて、描画のところであまり負荷がかかっていなかった場合、レイアウトのリビルドが起こっています。すごくよく走っているのも特徴ですね。GetComponentを打ちまくるので、GCがエグいことになります。

UIのリビルドを発生させないために

ということで対策として、UIのリビルドを起こさないことが大事です。

RectTransformに関連するUIシステムはできるだけ触らない。タッチした瞬間にリビルドが起こります。瞬間ではないですね。Dirtyが走ります。絶対にDirtyをしたくなければ、RectTransformはできるだけ触らないようにしてください。

Layout Propertyのほうにも触らないようにします。つまり、なにがいいたいかというと、UIが持っているテクスチャを差し替えると、レイアウトシステムはテクスチャが持っているUI Propertyを参照するので、リビルドが走ります。つまり、レイアウトシステムでテクスチャアニメーションを行うと、リビルドが走ってしまいます。UI Element関連は操作しないのが一番ですね。

これが一番面倒な話ですが、Mecanimのステートマシンというものがあります。このMecanimにAnimationClipをいっぱい突っ込んだらUIのアニメーションを作れるというチュートリアルがあって、これをやるとレイアウトを再構築してくれます。

Mecanimには、ステートマシン内で使っているPropertyに対して常に書き込むという挙動があります。アニメーションがいったん終了しているというレベルではなくて、まったく遷移を作っていないようなAnimationClipがあったとして、それがRectTransformにアクセスしていた場合、リビルドが起こります。

なので、Mecanimを使ってUIアニメーションを作る場合は注意が必要です。必ずそのUIシステムから弾いておく必要があります。

SimpleAnimationやAnimation(Legacy Animation)ならおそらくOKなんですが、Animationの場合はマテリアルの差し込みができません。

もしやる場合は、Tweenなどの外部アセットを使ったり、SimpleAnimationみたいなステートマシンに頼らないで、変な書き込みの挙動をしない機能使う必要があります。

もう1個、もしLayout GroupをScrollViewなどでやっていた場合、動かした瞬間にそのLayout Groupを誰が動かしたのかがわかります。なので、もしLayout Groupで、ScrollViewやトグルボタンなどを使っている場合には、誰が問題を起こしたかがわかるので、それを排除すれば大丈夫です。

最も確実なアプローチは、使わないことです。必要になれば使っても良いですが、こういう挙動があることは覚えておく必要があります。

その他注意すべきこと

その他にもいくつかあるので、ざっくばらんに紹介しましょう。

まず1個目。Raycast Target。UIを置くと、必ずRaycast Targetにチェックが入っているんですが、これがついているだけで毎フレームちょっとずつ負荷を上げてくれます。それはUIの判定用にチェックをしているからですが、これが2,700個あると、1.72secの負荷を計上してくれました。なので、本当に必要なとき以外はRaycast Targetのチェックを外しておけば、影響は受けません。

また、昔はそうではありませんでしたが、現在はRaycast Targetは同じCanvasに対してのみ参照するようになったので、触らない静的なUやGraphic Raycastを置かないという選択肢もあります。

World Space CameraのEvent Cameraというものがありますが、これに登録していないと必要になるたびに使います。悪くないことにFindTagはそれなりに効率の良いアプローチではありますが、特別なあれになったら取り出す処理です。ただ、できるだけ設定しておけば安くなります。

他には、Pixel Perfectというものがあります。これはPixel PerfectにUIをやってくれるシステムで、要するにレイアウト調整する機能です。

なので、これにチェックが入っていた場合、動かすと再レイアウトされるので、けっこう負荷をかけてくれます。前に弾幕シューディングを作ったときに「これがなければなんとかなるんじゃないか?」という話になったくらい、Pixel Perfectはレイアウトの負荷という面で問題になることがあります。

テクスチャが超高解像度で突っ込んでることがありますが、描画はやはりテクスチャと画面がだいたい同じぐらいのサイズであることが望ましいです。小さければ小さいほど、GPUで1回に処理できるテクスチャのサイズが増えます。

逆に、大きすぎるとちょっとずつしか処理はできません。例えばRGBA32など(非圧縮だと効率が悪いのですが)、ちゃんと圧縮してあれば一気に処理できるので効率が良いです。

それともう1個、UI Profilerはエディタのみで動作します。さんざん紹介しておいてなんですが、残念ながら実機の参照したプロファイラーでは動作しません。そういうものはFrame DebuggerやCPU Profilerなどで確認するか、エディタで引っ張ってくる必要があります。

Xcodeなどは、プロファイリングを行う場合には非常に便利です。負荷についてもエディタで確認することが多いと思いますし、UIに関してはそれでも問題ないと思いますが、やはりビルドしたほうがちゃんとしたデータが出ます。

これはiOS向けビルドだけでなく、Windowsでも良いです。ビルドしたことによってエディタ内でやっている処理を全部取り除くことができます。例えば、UIのシステムの場合、グラフィックの中に、Inspectorを触っているとガンガン更新する処理が入っています。なので、UIを確認する場合は、いったんビルドして出力しておくと、負荷を確認するという面では非常に良いです。

パフォーマンスの良いUIを作るポイント

最後にまとめです。uGUIでパフォーマンスのポイントは、基本的には動かさない。動かすのは最低限の影響に抑える。この2つでかなり速度が出やすくなると思います。

あとはドローコールとフィルレートとバッチング。よくあるケースで、親の仇みたいにこれらを削っていって、ドローコール1ですばらしいということがあるんですが、それによってほかのところにしわ寄せが起こっていることも多いので、良いバランスを取ってもらうことが重要かなと思います。

それと、これが一番いいたかったのですが……。

(会場笑)

プロファイラーは大事です。今話した内容でいくつかの対応策はありますが、実際マッチしない、もしくはバージョンが上がったことによって、あまり影響は受けなくなったということがあると思います。

例えば、最初に紹介したグラフィックとバッファのリビルドですね。あれは昔、すごく負荷が高かったんですが、今はメインスレッドからほとんど処理が剥がされて、ほとんど影響しなくなっています。なので、プロファイラーで実際にどうなっているのか、実際それが効果があるのかは確認してもらう必要があります。

そして、今後はUI Elementというのが来ます。CSSベースのちょっとHTMLっぽいなにか。かつ、アーティストに通用するツールなんですが、ここはもう少しお待ちください。現状はエディタ専用になっていますが、今後はRuntimeでも動くようになる予定です。Coming Soonです。

ということで、自分の話は以上になります。ご清聴ありがとうございました。

(会場拍手)

Canvasの粒度について

司会者:ありがとうございました。もしこの場で質問や気になるところがある方は、この場でご質問を受け付けれればと思います。いらっしゃいますか?

質問者1:Canvasの中にCanvasを入れるというお話がありましたが、その粒度についてはどのように考えていますか? 例えば、スマブラでキャラクター選択があったときに、キャラ一個一個を全部Canvasで分けるのか、それとも動くもの・動かないものに分けるのか。

山村:常に同時に動くもので分けるのが、おそらく一番効率的が良いです。

質問者1:同時に動く。はい、わかりました。ありがとうございます。

山村:ただ、当然のごとく、増やしすぎれば描画コストが上がるので、そこは注意が必要です。

質問者1:わかりました。ありがとうございました。

司会者:ありがとうございます。そのほかにある方はいらっしゃいますでしょうか? 

UIのパフォーマンス計測はいつからやるべきか?

質問者2:往々にして「いつからやるの?」という話があると思います。とくに3Dなどとても負荷が多いようなアプリケーションでは、プロファイリングが軽視されてしまう傾向があると思います。UIのプロファイリング、ないしはパフォーマンス計測は、いつからやるのがおすすめでしょうか?

山村:なるほど。難しい質問ですね。ある程度デザインが決まってくれば書き換えがつらくなってくるのはそのとおりなので、ある程度テクスチャが固まってきたら積極的に進めていくのが良いのではないかとは思います。ですが、昨今はそこまでUIを作らずにダイナミックに作っていることも多いですし、「いつ?」といわれるとなかなか難しい質問ではあります。「わからない」というのが正しいです。

早めにやったほうが良いと思いますが、ルールを説明して、ルールを守ってもらえるかぎりは大丈夫なので。でも、最後にどっちに振るのかは考える必要がありますね。それはどのタイミングでやれば良いのかな……。

質問者2:例えば「けっこういっちゃいました」ってなったときに、どこから手をつけるのがおすすめですか?

山村:基本的にはフィルレートが一番まずいことが多いです。次にUIリビルドです。この2つさえなんとかすれば、なんとかなるゲームは多いと思います。

バッチングはコストが高いですが、バッファは作って投げるだけなので、本当に投げるだけのコストですし、UIといってもせいぜい20ドローコールぐらいなので、Set PassもAtlas化させればほとんど1になります。とにかく、バッファのレイアウトの再構築を目の敵にしてもらったら問題ないです。

質問者2:ありがとうございます。

Canvasをオフにする際の設定

司会者:ありがとうございます。そのほかにご質問のある方はいらっしゃいますでしょうか?

質問者1:もう1個質問させてください。Canvasの扱いで、例えば、同じシーンなりヒエラルキーなりに3つCanvasがあって、場面ごとにタイトル、メニュー、ゲーム画面といった感じで切り替えようと思います。

そのときにCanvasをオフにする、描画しないようにするには、「GameObjectを無効にする」「Canvasを無効する」「Canvas GroupのAlphaをゼロにしてボタンを描画させない」など、いろいろ方法があると思いますが、どれが一番おすすめかということはありますか?

山村:すばらしい質問をありがとうございます。たぶん自分が入れ忘れたやつです。基本的には、まずはGameObjectを排除すべきです。GameObjectをオフにすると、その下にある全部のコンポーネントにオフというトリガーを送ってしまいますし、バッファも破棄されてしまうのでよくありません。

同様の話を聞いたりすると、Canvasのenable・disableをおすすめされることが多いですが、自分の手元で確認すると、大きいバッファをやった場合、そちらよりもCanvas GroupのAlphaをゼロにしたほうが安い感じがします。

ただ、逆にCanvasのサイズが大きくない場合、ちょっと小さめな場合は普通にCanvasのenable・disableを切ったほうが効率がよかったので、そこはケースバイケースになるかもしれません

質問者1:効率というのは、コードを書くにあたっての効率ですか?

山村:ごめんなさい。負荷が低かったです。Canvas Groupは、コンスタントに負荷が低くて、ちょっと高めだったんですが、Canvasを切ると、オフにしたときにガクンッと負荷が上がってしまったんですね。

質問者1:ああ、なるほど。

山村:それはCanvasが大きかった場合なんですが。

質問者1:全部にCanvas Groupを使うと、それはそれで負荷が上がるので、そうではなくて、でかいものにCanvas Groupで整理をして、小さいものはenableにすると。

山村:そうですね。ただ、これもケースバイケースになると思うので、これは見てもらって確認してもらったほうが良いかもしれないです。とにかくGameObjectはダメです。

質問者1:はい。基本方針がわかったので、よかったです。ありがとうございます。

山村:あっ、ごめんなさい。UIの完全にレイアウトを再構築する場合、GameObjectをオフにしてもう1回構築し直すほうが効率がよかったりします。

レイアウトを構築するルールが、GameObjectオンの下という条件があるので、これがオフになっていると、レイアウトのリビルド命令は走るんですが、GameObjectがdisableの場合はガン無視されます。なので、UIを完全に再構築するという観点から見ると、GameObjectは効率が良いです。

質問者2:わかりました。

山村:ここは本当にケースバイケースなんだよね。

司会者:ありがとうございます。それでは、発表は以上になります。山村さま、ありがとうございました。

(会場拍手)