Unityで高速なスクロールビューを作成する

#はじめに
今回実装した高速スクロールのソースはこちら
https://github.com/zi-su/ScriptLibrary/tree/master/DataScroller
uGUi標準のScrollViewの仕組みを使用しつつ、スクロール内の要素をデータとビューに分けて制御します。
スクロール内の各サイズがバラバラの場合にも対応しています。

#パフォーマンス比較
Unity2019.3.0b4 DX11
CPU i7-6700k 4.00GHz
RAM 16.0GB

標準ScrollView
Contentに1000個のGameObjectがある場合
VerticalLayoutとContentSizeFitterを使用しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/5542cbc7-1cb1-1137-3d35-d263f61c90ee.png)
スクロール中の処理負荷が恐ろしいです。

今回実装の場合
Contentに4個のGameObjectがあり、スクロールごとに位置計算と表示更新をする場合
スクロールさせた瞬間から処理負荷が上がっていますがわずかです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/08d51602-1b82-0069-293a-dca3b3d32ab6.png)

#uGUIのスクロールビューについて
uGUIで作成するScrollViewは手軽に利用できます。
ScrollViewを作成しContentゲームオブジェクトにVerticalLayoutやContentSizeFitterのコンポーネントを追加し設定することで自動レイアウトによる計算で見た目がいい感じになります。
ですが、スクロールビュー内の要素が100,200と増えるに従って問題点が多く出てきます。

##問題点
uGUIで作成するScrollViewは手軽に利用できますがContent以下の要素をScroll Viewのサイズでマスクしている作りになっています。
マスクしているために**画面に映っていない要素もすべて描画**しているために無駄に処理負荷が重くなり、要素数に比例してスクロールも処理が重くなります。
また実行中に何らかのスクロールビューを表示する際に必要な要素をInstantiateするとInstantiate自体のコストも大問題になります。
**つまりは要素数に比例して処理負荷が上がります。**
ScrollViewを使いVerticalLayoutやContentSizeFitterで自動レイアウトに計算を任せるのは非常に楽ですが、ゲーム中に実際に利用するには問題が多いです。

#高速にするには
以下の記事を参考にしています。
http://tsubakit1.hateblo.jp/entry/2015/01/21/233000
https://qiita.com/k7a/items/7db3a41e39923fd8a3b3

高速なスクロールを実装するために

+ 自動レイアウトによる計算を捨てる
+ ScrollView内に映るゲームオブジェクト数だけInstantiateする
+ スクロール時に位置計算を自分で行う
+ スクロール後に表示内容を更新する

を実装することになります。

##実装
Content以下のGameObjectの数が問題になるのでこのGameObjectを使い回すことで大幅な高速化が可能です。
実装の説明をしていくのでソースコードとともに見ていきます。

###コントローラ
スクロールの計算や表示の更新をScrollControllerBase.csで実装しています。
初期化でデータをリストに追加、ContentSizeの計算、ビューとして表示するGameObject数を計算します。
Updateで位置を監視しGameObjectが画面外に出たら先頭の要素を末尾に移動したり、末尾を先頭に移動したりさせています。

###データとビュー
スクロール対象をデータリストとして持ちGameObjectとして表示するビューをデータから更新します。
データとしてICellData.csを作成しContentに追加するサイズへのアクセサを持ちます。
CellDataBase.csに継承させてインスタンス化します。
ビューとしてICellView.csを作成しICellDataを受け取りビューに合わせた型にキャストしてビュー自体の更新を行います。
CellViewSimple.csをテスト用に作成してサイズ情報のみ更新しています。

###スクロールコントローラ設定
Modeでスクロールが水平方向か垂直方向かを設定します。
Spaceはスクロールの要素感の空き具合です。
Marginは先頭と末尾の空き具合です。
CellPrefabはスクロールに追加するGameObjectのプレハブです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/6d1acb6a-1b52-6c60-f663-b14b478324d9.png)

###AnchorとPivotの設定
位置計算を単純化するためにContentとContent以下のGameObjectについてAnchorとPivotを以下のように設定します。
スクロール方向が水平の場合
Content
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/743140c3-607a-cdfd-097d-80dfbbc49442.png)
Contentに追加するGameObject
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/259b06a5-7320-d334-78c7-25ebe01ec404.png)

スクロール方向が垂直の場合
Content
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/3bd82517-f9cb-be12-c5fd-25cd879d43f7.png)
Contentに追加するGameObject
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/bf1ae0cb-bb22-fc8b-9225-62abcbfdb88e.png)

#さいごに
アセットストアには更に高機能で素晴らしいスクロールのスクリプトはありますし、UIシステム全体を作っているアセットもあるので楽したいのなら買うのがいいです。
https://assetstore.unity.com/packages/tools/gui/enhancedscroller-36378
https://assetstore.unity.com/packages/tools/gui/doozyui-complete-ui-management-system-138361
今まで実装したことがなく新たな考えを身につけるなら技術力の成長に繋がる機会ということで車輪の再発明をするのもいいでしょう。