数値入力ピッカー
制作中のゲーム用に数値入力ピッカーが欲しいなと思ったものの、有償アセットを買うのはちょっと厳しく(忘れた頃にやってきた保険の更新……)、何かないかと調べるも、無限スクロールはあるけどピッカーのように特定の位置に止められるのは見つからない……
と、いうわけで、自作することにしました。
▽サンプル
慣性とかはないので、動きに改良の余地はまだまだあると思いますが、こんな感じで上下に無限スクロールするピッカーです。
▽作り方
ゲームオブジェクトはこんな感じ。
Text0~18がスクロールする数字です。
もう少し減らせたらと思いつつも、とりあえず保留。
サンプルでは、このプレハブを5つ並べて使っています。
また、上下の白いグラデーションも、並べたプレハブの上に被せてます。
グラデーションについては、UI Gradientを使用させていただいています。
ソースはこんな感じ。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using TMPro;
public class NumberPickerManager : MonoBehaviour, IEndDragHandler
{
// 定数
public float PICKER_NUM = 19; // 数値ピッカーの要素数
private float DEFAULT_POSITION = 0.5f; // スクロールの定位置
public int MAX_NUM = 9; // ピッカーの数値の最大値
// オブジェクト参照
public GameObject contentsParent; // 数値ピッカーの数値要素の親オブジェクト
// メンバ変数
public int currentNum = 0; // 現在の数値ピッカーの値
private int centerNum; // 数値ピッカーの中で中央に来るオブジェクトの順番。PICKER_NUM / 2 を四捨五入した値
// Start is called before the first frame update
void Start()
{
this.GetComponent().verticalNormalizedPosition = DEFAULT_POSITION;
centerNum = Mathf.RoundToInt(PICKER_NUM / 2);
}
// ドラッグ終了時
public void OnEndDrag(PointerEventData data) {
// 中央からの移動距離を求める
float pos = Mathf.Clamp(this.GetComponent().verticalNormalizedPosition, 0, 1);
int move = Mathf.RoundToInt((pos - DEFAULT_POSITION) * PICKER_NUM * -1);
// 数値ピッカーを移動後の状態に更新する
UpdatePickerNum(currentNum + move);
}
// ピッカーに指定した値を設定する
public void UpdatePickerNum(int value) {
currentNum = AdjustNumber(value);
// 移動後の値に各要素のtextを書き換える
for (int i = 0; i < PICKER_NUM; i++) {
contentsParent.transform.GetChild(i).gameObject.GetComponent().text =
AdjustNumber(currentNum + i + 1 - centerNum).ToString();
}
// スクロール位置を初期位置に戻す
this.GetComponent().verticalNormalizedPosition = DEFAULT_POSITION;
}
// 各要素に設定する数値を範囲内に収める
private int AdjustNumber(int num) {
while(num > MAX_NUM) {
num -= (MAX_NUM + 1);
}
while(num < 0) {
num += (MAX_NUM + 1);
}
return num;
}
}
大体ソース内のコメントに書いた感じなのですが、まとめると、
・ピッカーの親オブジェクト(NumberPicker)のスクロール位置は、
静止時には常に中央(0.5)固定
・ドラッグ終了時に、中央(静止時の位置)から上下にどれぐらい動いたかを取得して、
移動後の数値を算出
・移動後の数値が中央に表示されるように、Text0~18の各textを書き換える
みたいなことをしてます。
応用として、MAX_NUMの値を23とか59とかに変えると、時刻入力ピッカーも作れるはず(要オブジェクトの横幅調整)
AdjustNumber()内にそのまま書いちゃってるnum < 0の0をMIN_NUMとかにでもして、num -= (MAX_NUM - MIN_NUM + 1)、num += (MAX_NUM - MIN_NUM + 1)とかに修正したら1~9とかのピッカーにもできるはず。
あと、数値を配列の添え字として使えば、文字列の選択ピッカーにすることも多分できると思います。
ところで、ここまで書いてソース読み返して思ったのですが、PICKER_NUMのNUMは他のNUMが大体数値なのに対して個数なので、命名失敗したかも。
命名難しい……
▽参考