【Unity】UI最適化Tips

2019/04/04DEVELOP, Unity

Unityの最適化Tips「UI編」です。公式の最適化TipsであるSome of the best optimization tips for Unity UI – Unity で公開されているTipsの内容を確認しながら僕なりに検証してみます。重いアプリは大体、基本的な部分が出来ていないことが多いです。

環境

  • 確認バージョン 2018.3.8f1

Canvas を分ける

UnityのUIシステムはCanvas単位で更新(Rebuild処理)がかかるので、動くUIはCanvasを分けた方が良いという話。

ObjectAもObjectBも並列に配置する

Canvas
|_ObjectA
|_ObjectA
|_ObjectA
|_ObjectB // アニメーション再生

アニメーションさせるObjectBにCanvasをはさむ

Canvas
|_ObjectA
|_ObjectA
|_ObjectA
|_Canvas
  |_ObjectB // アニメーション再生

それぞれProfilerで処理負荷を確認してみます。ObjectAを1000個程度配置しています。

ObjectBを同じCanvasに配置して実行したとき

ObjectBを異なるCanvasに配置して実行したとき


前者はRenderOverlaysの数値が高く描画負荷が高くなっています。

Canvasを分けた方が差分で描画されるため軽くなります。ただし、Canvasが別なので同じSpriteAtlasを使用していてもPass Callが分かれます。Profilerで確認しながら対応するのが良いと思います。

そもそも動くUIとは何か?ですが、アニメーションによるPositionを変えたりは勿論のこと、ActiveのON/OFFやColorの変更も対象となります。シェーダーでUVを移動させたりしても同様です。

Canvasを分けるかどうかの基準は「頻繁に動くものがあるかどうか?」で良さそうです。

Camera.main の使用を避ける

MainCameraを参照したい時はMain.cameraを直接見るのではなくキャッシュして使いまわす。そしてCanvasのRenderModeが「World Camera」である場合は必ずCameraを設定する様にする。

「World Camera」のカメラ設定がNoneの場合

カメラに何も設定されていなくても、Canvasがイベントを受け取るためメインカメラを探そうとする。

unity3d-ui/GraphicRaycaster.cs at master · tenpn/unity3d-ui · GitHub

public override Camera eventCamera
{
    get
    {
        if (canvas.renderMode == RenderMode.ScreenSpaceOverlay
            || (canvas.renderMode == RenderMode.ScreenSpaceCamera && canvas.worldCamera == null))
            return null;

        return canvas.worldCamera != null ? canvas.worldCamera : Camera.main;
    }
}

Canvas毎に毎フレーム検索するため、塵も積もればそれなりのCPU負荷となります。

可能な限り Layout Group を回避する

Vertical Layout Groupなどを使用して子オブジェクトを配置する場合、頻繁に呼ばれる場合、なるべく使わない様にして、出来る限りアンカーの設定で済ませる。要素数が動的に変化するレイアウトでも、個人的に自動レイアウトをやめるのは、現実的な解決方法ではない気がするので、自動レイアウトのイベントがマイフレーム発火することの無い様、気を付ける程度で良いと思います。

賢いやり方で UI オブジェクトをプールする

はじめの「Canvasを分ける」でほぼ解決するので割愛。Canvas.Rebuildの範囲を絞りましょうという話です。

Canvas を非表示にする方法

gameObject.SetActive(false)ではRebuildが走ってしまうため、非表示にしたい時はCanvasコンポーネントを無効化します。描画されなくなるだけで、キャッシュされた頂点情報はされずにいるため、再びCanvasコンポーネントを有効すると再構築も走らず良いです。

UI 要素上でアニメーターを最適利用する

Animator再生中「動くUI」に該当します。値の変化が無いIdle中のアニメーションを再生している場合でも該当するため、無駄なアニメーションは再生しない、またはCanvasを分けるといったアプローチが必要です。

まとめ

ほとんどの場合、この辺りを守って実装すればパフォーマンスは保てると思います。もちろん、他にもフィルレートを抑える方法など沢山ありますが、まずは公式のTipsに則って最適化してみるのが吉だと思います。

Posted by kazupon