UnityでScroll Viewを使う際は、Contentの配下に大量のオブジェクト(たとえばButton)を配置してしまうと描画負荷が高くなります。
私の環境では、1000個程度なら問題がありませんでした(それでも負荷はかかっていました)が、1万個程度ならかなりの負荷になりました。
そのときのプロファイラーの結果は以下の通りです。
前半のスパイク部分がマウスでスクロールしたとき、幅広くピークがたっている部分はスクロールバーで移動しているときです。
特にスクロールバーを移動しているときは常時FPSが15を切ってしまっており、かなりの高負荷になっています。
そもそもオブジェクトを1万個も配置しない…といった話もあるかとは思いますが、 建築現場の部品単位で表示したい場合、部品点数が1万点以上になることもあり、何かしらの対処が必要でした。
今回は、大量のオブジェクトが対象でも、軽量にスクロールできるスクロールビューのつくり方を紹介します。
スクロールビューのつくり方
上の画像にスクロールビューのサンプルを示します。大雑把なつくり方は以下の通りです。
Unityのヒエラルキーで右クリック>UI>スクロールビューの作成を行います。
Scroll View/Scrollbar Horizontalを削除します(今回は不要)。
Scroll View/Scrollbar VerticalのRectTransformの下の値を0にします。Scrollbarを範囲いっぱいにするためです。
ScrollView/Viewport/Contentの下にヒエラルキーで右クリック>UI>ボタンで生成したButtonを配置します。
ScrollView/Viewport/ContentにVertical Layout Groupをアタッチします。これがあることで配下のButtonの位置を自動で調整してもらえます。
ScrollView/Viewport/ContentのVertical Layout Groupの子のサイズを制御で幅にチェックをいれて、チェックを外します。Buttonの幅を整えるためです。
ScrollView/Viewport/ContentにContent Size Filterを追加して、垂直フィットをPreffered Sizeにします。
ScrollView/Viewport/Content/Buttonを必要な個数だけ複製します。
どうやって軽くするか
先ほどの画像をよくみてみると、Buttonは0~6までの7つのみ表示されています。
Buttonで表現したい対象がいくら大量にあっても、画面に表示されるのは7つのみということです。
つまり、表示に必要な最低個数を使いまわすことで軽量に動作するスクロールビューが実現できます。
動作イメージ
無限スクロールのブログ用です。 pic.twitter.com/5TduWbPpP8
— ぜー (@zhei_) 2022年1月24日
こちらの動画のように、Buttonの数は増やさずにButtonの数字を増減させています。 (動画では数字の切り替わりが飛ばし飛ばしになっていますが、以下のコードでは順番になるよう修正しています)
サンプルコード
以下のサンプルコードを任意のオブジェクトにアタッチして、ContentのTransformとScrollbarのスクリプトを参照すると動作します。
using UnityEngine; using UnityEngine.UI; using UniRx; public class SimpleScrollViewConroller : MonoBehaviour { [SerializeField] private Transform contentTr; [SerializeField] private Scrollbar scrollbar; private Text[] texts; private int current; // 現時点のButton群の先頭の番号 private int final; // 最後方のButton群の先頭の番号 private int length; // 表示するButtonの数 private int size = 10000; // Buttonの最大数 private void Start() { texts = contentTr.GetComponentsInChildren<Text>(); length = texts.Length; for (int i = 0; i < length; i++) { texts[i].text = i.ToString(); } final = size - length + 1; // 次に進む scrollbar.ObserveEveryValueChanged(x => x.value) .Where(x => x < 0f) .Where(_ => current != final) .Subscribe(x => { scrollbar.value = 1; current++; SetNumber(); }) .AddTo(this); // 前に戻る scrollbar.ObserveEveryValueChanged(x => x.value) .Where(x => x > 1f) .Where(_ => current != 0) .Subscribe(x => { scrollbar.value = 0; current--; SetNumber(); }) .AddTo(this); } private void SetNumber() { for (int i = current; i < current + length; i++) { var j = i - current; texts[j].text = i.ToString(); } } }
注意事項
実際に利用する際には、 SetNumber()でButtonのTextを変更するだけではなく、押したときの動作を切り替えるようにつくりこむ必要があります。
また、今回の方法ではスクロールバーの位置がContent配下にあるButtonに対する相対的な位置を表してしまっているため、 全体の範囲に対して、スクロールバーをドラッグして移動することができない状態です。
この問題に関して、独自のスクロールバーを実装することで解決したので、以下の記事で紹介しました。
【無限】Unityで大量のオブジェクトでも軽量にスクロールできるスクロールビューをつくろう(その2) - 建設とUnity