この記事でのバージョン
Unity 2018.4.6f1
はじめに
公式でUnityのお役立ち情報を配信しているUnity for Proというサイトがあるのですが、
(ライセンスのProではなく、実務で使えるという意味でのPro)
その中の「Unity UI の最適化に関するヒント」といういかにも重要そうな記事があったので、
今回は勉強がてら、この記事を自分なりにまとめ直してみました!
目次
- はじめに
- 目次
- 1. 頻繁に動くものはCanvasを分ける
- 2. 無駄なRaycastをなくす
- 3. Camera.mainに何度もアクセスしない
- 4. Layout Group(Scroll Rect)を使わない
- 5. オブジェクトプール時の処理の順番に気を付ける
- 6. Canvasを非表示にする時はコンポーネントを無効にする
- 7. 変更がないAnimator(Animation)は使わない
- おわりに
1. 頻繁に動くものはCanvasを分ける
Canvasは要素(子に含まれるもの)の中に1つでも変更があると、Canvas全体、
つまり全ての要素の描画のための計算をやり直します。
Canvas の要素に 1 つでも変更があると、変更された要素の描画を最適化するために、Canvas 全体を再分析する
Unity UI の最適化に関するヒント – Unity for Pro
逆に言うと変更さえなければ、計算が起こらないため軽いという事でもあるので、
もし頻繁に動くものがあればCanvasを分ける方が良いでしょう。
ちなみに、以前この比較記事を書いてるので実際の数値が気になる場合は、参考になるかと思います。
2. 無駄なRaycastをなくす
Canvasにデフォルトで付いてるGraphic Raycasterですが、
これはタッチやクリックを判定するために必要なものです。
もちろんタッチやクリックの必要がないUIの場合でも、判定は行われてしまうので、
そういう時は無駄な処理が行われないようGraphic Raycasterを削除しましよう。
また、ImageやTextなどに付いてる「タッチやクリックが出来るか」を設定するRaycast Targetも同様で、
タッチやクリックの判定が必要ない時は無効にすると無駄な負荷が減ります。
(デフォルトでは有効になってるので、注意が必要)
3. Camera.mainに何度もアクセスしない
Camera.mainを使う事で、MainCameraというTagが付いてるカメラ
(最初からシーンに配置されてるカメラ)を簡単に取得する事が出来ますが、
実はCamera.mainはアクセスするたびにタグで検索してるので、重い処理だったりします。
_mainCamera = Camera.main; //↓と同じような事をしている _mainCamera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>();
AwakeやStartなどで1度だけ取得する分には問題ありませんが、
Updateなどで何度もアクセスするのは避けましょう。
uGUIの話に戻ると、CanvasのRender ModeをWorld Spaceにすると
Event Cameraという項目が表示され、このデフォルトがNoneなのですが、
なんとNoneの状態だとCamera.mainにアクセスします。
しかも 1 フレームあたり 7 〜 10 回もアクセスするという事なので、Noneのまま使うのは避けましょう。
4. Layout Group(Scroll Rect)を使わない
TextやImageなどの要素を等間隔に並べたい時に便利なLayout Groupというコンポーネントがあります。
このLayout Group、なんと使うとGetComponentsが実行される回数が増えて重くなるので、
使わない方が良いらしいです。
自身のレイアウトをダーティとしてマークする UI 要素は、少なくとも GetComponents を 1 つ以上呼び出します。この呼び出しは、まずレイアウト要素から、親の有効なレイアウトグループを検索します。該当するレイアウトグループが見つかった場合は、検索を停止するか、ヒエラルキーのルート階層に到達するかのいずれか早い方で、ヒエラルキー内の Transform を上っていきます。したがって、各レイアウトグループは、それぞれの子レイアウト要素のダーティ化処理に GetComponents の呼び出しを 1 回追加することになり、ネスト化されたレイアウトグループのパフォーマンスを極度に悪化させます。
Unity UI の最適化に関するヒント – Unity for Pro
もし、等間隔に並べたい場合はそういうコードを自分で書き、
それを適宜(要素が追加された時など)実行するようにすると良いでしょう。
ちなみにスクロールの実装に使うScroll RectもLayout Groupなので、これも使わない方が良いようです。
5. オブジェクトプール時の処理の順番に気を付ける
オブジェクトを削除(Destroy)したり、新しく作ったり(Instantiate)する処理は重いので、
何度も使うオブジェクトを再利用するために、
使い終わったものを一旦非表示(gameObject.SetActive(false))にして置いておき、
また使う時に表示するというオブジェクトプールという方法があります。
そのオブジェクトプールをuGUIで使う際に気を付ける事があります。
例えば以下のように通常のCanvasと、オブジェクトプール用のCanvasが違う場合、
使わなくなったものは非表示にしてからCanvasを移動させた方が良いです。
//使わなくなったものを非表示にしてから、 _text.gameObject.SetActive(false); //Canvasを移動 _text.transform.SetParent(_objectPoolCanvas.transform);
こうすることで、変化があったのはCanvasだけになり、
ObjectPoolCanvasでは変更処理が走らないので、負荷が減らせます。
(移動してから非表示にすると両方に変更処理が走る)
同様に、再度表示する場合は移動してから表示するようにします。
//ObjectPoolCanvasからCanvasに移動してから、 _text.transform.SetParent(_canvas.transform); //表示 _text.gameObject.SetActive(true);
6. Canvasを非表示にする時はコンポーネントを無効にする
Canvasを非表示にする際、オブジェクトごと非表示にすると
_canvas.gameObject.SetActive(false);
再度表示する際に再構築(どういう描画をするかの計算)が発生してしまいます。
さらにOnDisableやOnEnableも発生するので、余計に処理が重くなります。
しかし、Canvasのコンポーネントを無効にして非表示にすれば、
_canvas.enabled = false;
再構築やOnDisable、OnEnableも発生しないので、
Canvasを非表示にする時はコンポーネントを無効にしましょう。
7. 変更がないAnimator(Animation)は使わない
Animator(Animation)はuGUIでも使えますが、
何も変更がないアニメーションだったとしても、実行中は常にCanvasの再構築が行われています。
なので、Idle的な何も変化がないアニメーションはuGUIでは使わず、
Animatorを無効にするようにしましょう。
もしくはAnimator自体を使わず、
DoTweenなどのTween系アセットで動かしたい時だけ動かすというのもありです。
DOTween Pro - Asset Store |
おわりに
uGUIは楽に実装出来る反面、間違った使い方をすると簡単に負荷が増えてしまうので、
今回のような事を知っておくのはかなり大事だったりします。
また、Layout Groupのように標準機能が罠ということが
uGUIにかかわらずUnityには結構あるので、これも大事な話です。