[unity] ScrollRectを強化するTableScrollView
unity の ScrollRect は便利ですが、アイテム数が増えていくとどんどん重くなるのがネックです。
またゲームだと、キーボード(パッド)入力にも対応する必要があります。
この要望を拡張してくれる追加コンポーネント、TableScrollView を紹介します。
GitHub
特徴
ベースは ScrollRect
ScrollRect のいいところは残しつつ、いくつかの UX 改善を行っています。
- スクロールバーが自動フェードアウト(機能オンオフ可能)
- 選択されたアイテムがスクロールストップ時、自動的に適切な位置に補正する
アイテム数が大量に増えても処理落ちしない
最初に表示エリアに必要な最低限のアイテム(Node と呼びます)バッファがヒエラルキーに確保され、以後はそのバッファを書き換えつつスクロールエリアを表現します。
実際に表示する項目が1000になっても、バッファは表示エリア分(多くても20程度でしょう)なので、大幅な速度改善が期待できます。
キーボード / パッド などのコントローラー入力用イベント
入力はイベントとして外に出しているので、各々アプリ側で使っているキー入力システムを当てはめることができます。
unity6 より下でも(多分)使用可能
いくつか unity の都合で書式の変わったコードさえなんとかすれば、使える……(はず)。
使い方
SampleScene2.unity を実行してください。必要な作業は3つです。
1. ノードを作成する
アイテム1つ分の Prefab(表示用)と、それを制御するノードクラスを記述します。
こんな風に表示用の Prefab を作って……。
using UnityEngine.UI;
public class NodeScene2 : TableNodeElement
{
[SerializeField]
TextMeshProUGUI No = null;
[SerializeField]
TextMeshProUGUI Desc = null;
[SerializeField]
Image Icon = null;
[SerializeField]
Image Focus = null;
[SerializeField]
Sprite[] IconSprites = null;
/// <summary>
/// フォーカス ON/OFF の表示をここに記述する
/// </summary>
public override void onEffectFocus(bool focus, bool isAnimation)
{
Focus.color = new Color(0,0,0, focus == true ? 0.2f : 0.1f);
}
/// <summary>
/// 行の表示更新通知があった場合、ここで表示を更新する
/// </summary>
public override void onEffectChange(int itemIndex)
{
var row = (TestScene2.Row)table[itemIndex];
No.SetText("Line: " + row.No.ToString("00"));
Desc.SetText(row.PlaceName);
Icon.sprite = IconSprites[row.No % IconSprites.Length];
this.name = No.text;
}
}
TableNodeElementをベースクラスとしたノードクラスを作成し、アタッチします。
2. ScrollRect に TableScrollViewer をアタッチする
ScrollRect と同じ GameObject に TableScrollViewer をアタッチし、先ほど作成したノードをドラッグ、スクロール方向が縦なら Vertical、横なら Horizontal を選んでください。
3. ビューに表示するデータリストを登録する
TaseScene2.cs をご覧ください。
using System.Collections.Generic;
using UnityEngine;
public class TestScene2 : MonoBehaviour
{
[SerializeField]
TableScrollViewer viewer;
public class Row
{
public int No;
public string PlaceName;
}
List<Row> rows = new List<Row>()
{
new Row() { No = 1, PlaceName = "Amsterdam" },
new Row() { No = 2, PlaceName = "Bangkok" },
new Row() { No = 3, PlaceName = "Chicago" },
new Row() { No = 4, PlaceName = "Dubai" },
new Row() { No = 5, PlaceName = "Edinburgh" },
new Row() { No = 6, PlaceName = "Frankfurt" },
new Row() { No = 7, PlaceName = "Geneva" },
new Row() { No = 8, PlaceName = "Hong Kong" },
new Row() { No = 9, PlaceName = "Istanbul" },
new Row() { No = 10, PlaceName = "Washington, D.C." },
};
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
viewer?.Initialize();
viewer?.SetTable(rows);
viewer?.OnSelect.AddListener(OnSelectVertical);
viewer?.OnKeyDown.AddListener(OnKeyDown);
}
public void OnSelectVertical(List<object> table, int itemIndex, int subIndex, bool isCancel)
{
Row row = (Row)table[itemIndex];
if (subIndex < 0)
{
Debug.Log($"selected: {row.No} {row.PlaceName}");
}
}
public void OnKeyDown(TableScrollViewer.KeyDownArgs args)
{
if (Input.GetKeyDown(KeyCode.Space) == true)
args.Flag = TableScrollViewer.eKeyMoveFlag.Select;
else if (Input.GetKeyDown(KeyCode.UpArrow) == true)
args.Flag = TableScrollViewer.eKeyMoveFlag.Up;
else if (Input.GetKeyDown(KeyCode.DownArrow) == true)
args.Flag = TableScrollViewer.eKeyMoveFlag.Down;
}
}
List<Row> の型に決まりはないので、自分で好きなように設定してください。
Row 1つ分がノード1つ分になります。
ノードの表示側では、このように定義しています。
/// <summary>
/// 行の表示更新通知があった場合、ここで表示を更新する
/// </summary>
public override void onEffectChange(int itemIndex)
{
var row = (TestScene2.Row)table[itemIndex];
No.SetText("Line: " + row.No.ToString("00"));
Desc.SetText(row.PlaceName);
Icon.sprite = IconSprites[row.No % IconSprites.Length];
this.name = No.text;
}
項目の説明
TableScrollViewer のインスペクターに表示される項目について説明します。
設定
-
Source Node:
作成したノードをここにドラッグします。 -
Orientation:
縦方向であれば Vertical、横方向であれば Horizontal を指定します。 -
Alignment:
この項目は実装が適当なので「スクロールしないビューのみ機能」します。
TableScrollViewer といいつつ通常のボタンメニューでも使えるので、そういう場合には有用な項目です。 -
Padding Top / Bottom:
表示エリア上(左)と下(右)の余白です。 -
Spacing:
ノードとノードの間の余白です。 -
Scroll Time:
スクロール速度。小さいほど速い。 -
Skip Index By Page Scroll:
キーやパッドでページスクロール要求(eKeyMoveFlag.Page???)された時、一度に移動するアイテムの数。 -
Scrollbar Auto Fadeout:
リストがスクロールすると表示されるスクロールバーを自動的に消します。 -
Absorption Target:
説明が難しい……オンにしておくと、とりあえず UX が上がります。
(スクロールのストップ位置を使いやすいよう補正する、というニュアンス) -
Select After Focus:
チェックを入れると「1度めフォーカス > 2度目実際に項目選択」の2ステップになります。 -
Easy Focus For Mouse:
マウス使用時、クリック選択しなくても自動的にフォーカスされるようになります。 -
Disabled After Select:
選択後、入力を受け付けなくなりますが、InputEnabled(true) で戻す必要があるので、使い勝手が悪いです……。
イベント
-
OnSelect():
項目が選択されると発生します。引数でどれを選択されたかが分かります。 -
OnKeyDown():
キー入力に対し、View がどう動くかを設定します。
Input でも、InputSystem でも、お好みの入力クラスをお使いください。 -
OnCursorMove():
フォーカスが移動した時に発生するイベントです。
フォーカス移動時の描画はノードクラスで定義しますが、カーソル音が必要であればこちらで定義してください。 -
OnCheckSelectable():
選択されようとしている項目が、選択可能かどうかを返すイベントです。
たとえば選択項目が0-1-2-3-4とあり、1だけ選択できない場合、Index が1の項目ではresult.Enabled = false
を返すようにしてください。
その他
ノード内にサブノードを作ることもできるため、m列xn行のようなメニューも作ることが可能です。SampleScene.unity を参考にしてください。
Discussion