Raspberlyのブログ

Raspberlyのブログ

Unityネタをメインとした技術系ブログです。にゃんこ大戦争や日常なども。そろそろブログタイトル決めたい

勉強会レポ : Unityでパフォーマンスの良いUIを作る為のTips

 

勉強会のレポート(メモ)です。
参加したのはこちら、「Unityでパフォーマンスの良いUIを作る為のTips 」

techxgamecollege.connpass.comハッシュタグ : #techxgamecollege

 

f:id:Raspberly:20181213014703j:plain

 

 

 

スポンサー様

主催は株式会社テクロスです。

東京で新規事業を開発予定です。興味があればどうぞ。

TECHxGAME COLLEGさんではいろいろな勉強会が開催されており、
過去にはUnityのアニメーションについての勉強会もありました。

raspberly.hateblo.jp

 

 

 

 

Unityでパフォーマンスの良いUIを作る為のTips

スライド

www.slideshare.net

 

 

話す内容

・UIのおさらい
・パフォーマンスのために注意すること
なんとなく使うとなんとなくしか性能がでません
 ・バッチング
 ・フィルレート
 ・リビルド(ボトルネックになりがち)
その他

 


UnityUI

・4.6で登場しました(それまではNGUIが主流だった)
・NGUIの人を雇って作りました
・通称UGUI

 

さまざまな機能があります

レイアウト
アンカーとポジションを使ったもの
画面解像度の適応に便利

自動レイアウト
レイアウトが破綻しないようにしてくれる
難しいです

ビジュアル表現
シェーダーをぶち込む

インタラクション
ボタンを押したら何かする とか(イベント)

なんとなくそれなりのものが作れてしまうシステムなんですが。
パフォーマンスは勝手に最適化してくれるわけではありません。


柔軟性とパフォーマンスのバランスが大事です。
uGUIはパフォーマンスよりも柔軟性に寄っています。
ではパフォーマンスに寄せるにはどうするか


バッチング

描画処理は後ろからテクスチャをぺたぺた張っていきます。
テクスチャにマテリアルとポリゴンをくっつけて描画するが、これを複数やるのは無駄が多い。
なのでuGUIではCanvasにまとめて一回で描画するシステムがあります。
これがバッチングです。

 

ルール

・同じCanvas上でなければなりません

・一括描画できるのは同じテクスチャのみ。
問題はバッチングが途切れることがあります。

f:id:Raspberly:20181213005433j:plain
図では、アーチャー、ライダー、アーチャーで描画されます。
このように別のテクスチャがあるとバッチングが解けてしまいます。
ちなみにtsubakiさんはランサー推しです

 

・マテリアルが違う場合も途切れます

 

・座標のZ軸が同じでないとだめ
タイトル画面などで、導入のUIを手前に置くとかだとバッチングが途切れてしまいます。


しかし注意しても途切れることもあります。
そういう時は途切れる部分を探しましょう。

 

UI Profilerで確認しましょう

バッチングの有無とどうやってバッチングされているかを見ることができます。

バッチングされているUIを選択するとシーンビューでバッチングされてるものがまとめて確認できます。

バッチできない理由も表示してくれます(テクスチャが違うなど)
対策としてはアトラスの機能で1枚でまとめるといいでしょう。

f:id:Raspberly:20181213010058j:plain

テクスチャが同じなので、まとめて表示できます。

ただ問題があります。
画像に変なものが混じります、これはアトラスがみっちりと詰め込んであるからです。
unityは基本矩形で取るので、タイトパッキングを有効にすることで余裕をもって配置することができます。

f:id:Raspberly:20181213010235j:plain

これは最近できるようになったものです。(Unity2018.3から)
今は矩形だけでなくメッシュも使えるようになりました、ポリゴンで描画します。

バッチング対策

・条件を満たす
・テクスチャをパックするマスクの使用は抑える(バッチングが途切れます)

 

 

 

 

 

 

フィルレート

unityはポリゴンに色をぬって表現していきます。
1ピクセルずつ塗っていきます 基本的には透明も含めて塗っていきます。

ここで考えることがあります、透明も塗られるということです。
たくさんUIがあって重なっている時は、透明の所も複数回塗られていきます
パット見わかりませんが重ね塗りされています。
これをオーバードローと言います。

f:id:Raspberly:20181213010632j:plain

 

どうやってこれを確認するか、UI Profilerを使いましょう。
色が赤くなるとヤバイところ。問題はこれが広い場合、画面全体が赤いとまずいです。

 

対策

・不要なUI、見えてないとこを削りましょう
・重なる部分を削る
・UIには真ん中をくり抜く機能があります(Fill Center)
・オーバードローしても問題ない軽量なシェーダーを使う(マスクを抜く 頂点カラーを抜くなど)

ただし、asset bundleを使って配信した場合、依存関係はしっかりね。

 

現状Unityでシェーダーの負荷を確認することはできません。
各デバイスのプロファイラーで確認しましょう。
XCodeならできます。

 

 

 

 

 

 

UIのリビルド(ジオメトリ)

大事な部分です、ぶっちゃけ前の二つはいらない。

基本的にunityはまとめて描画します。
Canvasが作ったバッファは使いまわすことができるので、ほとんど負荷がかかりません。

ただ使いまわすということは同じデータしか使えないということ。
バッファのデータ(マテリアルやテクスチャ)が変わった場合、再構築が必要になります。
これをリビルドといいます。

ただしバッファの再構築は即時されるわけではない。

 

 

更新される条件

・UIのenable/disable
・マテリアルの変化
・RectTransformのサイズの変化
・Transformの親の変化
ここで問題があります、1つ変わるとCanvasのすべてのUIを再構築する必要があります。
たとえ1つでも変わっただけでも全部作り直しです。

ただ幸いなことにメインスレッドには負荷はそれほどでもない。
ただし、Canvasのサイズによってはジオメトリの再構築に時間がかかります。
バッチング時のソートに時間もかかることも(一箇所につまってる場合など)

 

これはどうやって確認するか、UI Profilerでやりましょう。
リビルドが走るといっぱい山が起きます。

Profilerでも紫色のがびょーんと伸びているとやべーです。
リビルドが起こるのはいいが、だれが起こしたかが重要です。
CPU Profilerでリビルドの発行をしてるやつを探します。

 

 

UIは基本動くもの

じゃあどうやるか、手っ取り早いのはCanvasを分割すること。
キャンバスの中にキャンバスを入れることもできます。サブキャンバスといいます。
これをやると影響範囲を減らすことができます。

動かすCanvas動かないCanvasで分けるといいでしょう。
こうすることで動かすCanvasのみリビルドが走ります。
Canvas間ではバッチングが起こらないので、ドローコールはどうしても増えてしまいます。


もういっこあります。
動くUIはImageのSprite Meshは避けましょう。
バッチングをするという観点からみるとコストが高い。

 

親UIを動かすとバッチングが途切れます、大きな問題です。
大量のimageを持つ親をうごかすとがくがくになる、
これもUI Profilerで確認しましょう。

 

これは対策がないです

要素を減らしたり、要素をプーリングして最小限のUIにしたりなど。
ScreenSpaceCameraも同じで動かさないようにした方がよいです。

 

 

 

 

 

UIのリビルド LayoutGroup

こっちもリビルドを起こします。

まずおさらい、レイアウトグループはILayoutGroupが整列してくれます。

この機能を使うと、要素が増えたり、大きさが変わっても追従してくれます
柔軟性が高い代わりにパフォーマンスが。。。

 

その前にDirty Systemについて

汚れたら変更されたと認識して再構築するシステム。

サイズが変わると、Dirtyが入り、LayoutGrouoにリビルドを申請し調整してくれる。
Layoutの中にLayoutがあった場合、親の親の親・・・に通知といいた風に調整してくれます。

 

 

問題の話

これは賢い機能だが、どうやっているのか。
GetComponentsでやっています、親のコンポーネントを全部もってきてLayoutGroupがあれば・・・といった風にやっていく。
たくさんあればあるほどやばい・
当然、親から子へ戻るときもGetComponents。

 

GetConmpnentはやりたくないと思いますが、実はそれほどでもない。
致命的なのは、DirtyのUIが一つでも混じることで、毎フレームUIのリビルドが走ること
これは一番問題になります。
Mac book proでもリビルドに12secかかるほど。

 

確認方法はUI Profiler。
レイアウトのリビルドはメインスレッドで動きます。
描画のとこであまり負荷がかかっていない場合はレイアウトを疑いましょう。

 

 

 

対策

・RectTransformに関係するものは操作しない。
タッチしただけでDirtyが走ります。
テクスチャの変化でも走ります。

・UI ElementやLayoutPropertyなどは操作しない

・MecanimのステートマシンでAnimationClipを入れない
再生していないAnimationClipがあったとして、それがRectTransformにアクセスしただけでリビルドが起こります。
SImpleAnimationなら多分OK。Legacyなども。

最もいいのはLayoutGrouopを使わないことです。

 

 

 

 


その他

・RaycastTargetは外しましょう 毎フレーム負荷がかかります。

・World SpaceCameraのEvent Cameraにはカメラを登録しておきましょう。
 実はFindTag(MainCamera)が呼ばれます。

・PixelPerfect設定は動かないカメラのみ設定しましょう。

・テクスチャは画面解像度に合ったものを使いましょう、可能な限り圧縮しよう。

・UI Profilerはエディタのみ、実機のプロファイラでは動きます。

XCodeなどのプロファイラは便利です

・負荷を確認するのならビルドしたほうがいい
 PC向けでもいい 余計なノイズを減らすことができます。
 Inspectorにもガンガン更新処理がかかっています。

 


まとめ

ポイント
・基本動かさない、動かすなら最低限に。
・ドローコールとフィルレート、バッチングで良いバランスを。
 ドローコールを極限まで減らすと他のところでしわ寄せがある場合も
・気を抜くな!だれも信用するな!プロファイラーを手放すな!

 

UI Elementというのが今後きます。
もうちょっとお待ちください。

 

 

 


質問

Q : 動くものと動かないのCanvasはどうわける?

A : 同時に動くもので分けましょう

 

Q : UIは軽視されがちだが、いつからプロファイリングに力を入れるべき?

A : テクスチャが固まってきたらやった方がいいですが、基本的に早めにやった方がいい

結構開発が進んだ時は?
基本的にフィルレート、UIリビルドです。
レイアウトの再構築を目の敵にするような感じでやるとよい。

 

Q : Canvasの扱い
描画をしない場合はいろいろ方法がありますが、どういうのがいいの?

A : GameObjectはOffにすると、その下全部がオフになり、バッファも消えるのでよくない。
CanvasのEnableとかおすすめされるが、CanvasGroupを0にしてもよい。
ただしcanvasの大きさ次第。

でかいCanvasはCanvasGroupで、小さいCanvasはEnableでやった方がいいがよい。
この辺はケースバイケース、プロファイルで見ましょう。

 

 

 

 

タイムライン