はじめに
Unity の標準 Slider コンポーネントは、1 つの値を変更することしかできません。しかし、ゲームや UI で「最小値と最大値を設定したい」といった場面は多くあります。
例えば: - オーディオ設定で、特定の周波数帯を選択 - ゲーム内のフィルター機能(価格の範囲選択など) - キャラクターのパラメータ設定
こうした用途に対応するため、 MinMaxSlider を作成し、最小値と最大値を調整できるスライダーを実装します。
MinMaxSlider の実装
以下のスクリプトでは、Selectable を継承して MinMaxSlider を作成し、2 つのハンドル(最小値用と最大値用)を持つスライダーを実装します。
using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using UnityEngine.EventSystems; [RequireComponent(typeof(RectTransform))] public class MinMaxSlider : Selectable, IDragHandler { [SerializeField] public RectTransform _handleMinRect; [SerializeField] public RectTransform _handleMaxRect; [SerializeField] public RectTransform _fillRect; [SerializeField] private float _minValue = 0f; [SerializeField] private float _maxValue = 1f; [SerializeField] private float _minLimit = 0f; [SerializeField] private float _maxLimit = 1f; [SerializeField] private bool _wholeNumbers = false; private bool _isDraggingMin = false; private bool _isDraggingMax = false; public class SliderValueChangedEvent : UnityEvent<float, float> {} [SerializeField] private SliderValueChangedEvent _onValueChanged = new SliderValueChangedEvent(); public SliderValueChangedEvent OnValueChanged { get => _onValueChanged; set => _onValueChanged = value; } protected override void Start() { base.Start(); UpdateHandles(); } public override void OnPointerDown(PointerEventData eventData) { base.OnPointerDown(eventData); if (RectTransformUtility.RectangleContainsScreenPoint(_handleMinRect, eventData.position, eventData.pressEventCamera)) { _isDraggingMin = true; } else if (RectTransformUtility.RectangleContainsScreenPoint(_handleMaxRect, eventData.position, eventData.pressEventCamera)) { _isDraggingMax = true; } } public override void OnPointerUp(PointerEventData eventData) { base.OnPointerUp(eventData); _isDraggingMin = false; _isDraggingMax = false; } void IDragHandler.OnDrag(PointerEventData eventData) { Vector2 localPoint; if (!RectTransformUtility.ScreenPointToLocalPointInRectangle( transform as RectTransform, eventData.position, eventData.pressEventCamera, out localPoint)) return; float width = ((RectTransform)transform).rect.width; float normalizedValue = Mathf.InverseLerp(-width / 2, width / 2, localPoint.x); float value = Mathf.Lerp(_minLimit, _maxLimit, normalizedValue); if (_wholeNumbers) { value = Mathf.Round(value); } if (_isDraggingMin) { if (value > _maxValue) { (_minValue, _maxValue) = (_maxValue, value); _isDraggingMin = false; _isDraggingMax = true; } else { _minValue = Mathf.Clamp(value, _minLimit, _maxValue - (_wholeNumbers ? 1 : 0.01f)); } } else if (_isDraggingMax) { if (value < _minValue) { (_maxValue, _minValue) = (_minValue, value); _isDraggingMax = false; _isDraggingMin = true; } else { _maxValue = Mathf.Clamp(value, _minValue + (_wholeNumbers ? 1 : 0.01f), _maxLimit); } } UpdateHandles(); } private void UpdateHandles() { float width = ((RectTransform)transform).rect.width; _handleMinRect.anchoredPosition = new Vector2((_minValue - _minLimit) / (_maxLimit - _minLimit) * width, _handleMinRect.anchoredPosition.y); _handleMaxRect.anchoredPosition = new Vector2((_maxValue - _minLimit) / (_maxLimit - _minLimit) * width, _handleMaxRect.anchoredPosition.y); _fillRect.offsetMin = new Vector2(_handleMinRect.anchoredPosition.x, _fillRect.offsetMin.y); _fillRect.offsetMax = new Vector2(_handleMaxRect.anchoredPosition.x - width, _fillRect.offsetMax.y); OnValueChanged.Invoke(_minValue, _maxValue); } }
まとめ
MinMaxSlider を使用することで、範囲を選択できるスライダーを簡単に作成できます。ゲーム UI や設定画面で便利に使えるので、カスタム UI を作成する際に試してみてください!
