Unityのスライダーを、指定した任意の位置に自動で吸着(スナップ)させる

音量調整や設定画面のスライダーで、「キリのいい値にぴったり合わせたい」と感じたことはありませんか?そのような場面で役立つのがスナップ(吸着)機能です。特定の値に近づくと自動で吸い付くため、ユーザーが意図した値に合わせやすくなります。
本記事では、スナップさせたい位置と吸着距離を自由に設定できる実装方法を解説します。
- – Unityのスライダーに吸着(スナップ)機能を付けたい人
- – 等間隔ではなく、任意の位置にスナップさせたい人
- IPointerUpHandler` の使い方を知りたい人
- – 音量や設定値のUIをより使いやすくしたい人
Sliderのスナップとは?

スナップとは、ある値に近づいたときに自動でその値に吸い付く動作のことです。
Unityの標準Sliderコンポーネントにはスナップ機能が搭載されていません。スライダーをドラッグすると連続した値が取れるだけで、特定の値に吸着させるにはスクリプトで制御する必要があります。
Sliderの準備

UnityのHierarchyウィンドウからUI > Slider を追加してください。
また、スナップした値をテキストで表示するため、UI > Text – TextMeshPro も追加します。
ソースコード
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using TMPro;
public class SliderSnap : MonoBehaviour, IPointerUpHandler
{
public Slider slider;
public TextMeshProUGUI positionTMP;
[Header("スナップ設定")]
public float[] snapPoints = new float[] { 0.5f, 0.2f }; // 正規化値(0~1)
public float snapThreshold = 0.02f;
void Start()
{
slider.onValueChanged.AddListener(OnValueChanged);
UpdatePositionDisplay();
}
void OnDestroy()
{
slider.onValueChanged.RemoveListener(OnValueChanged);
}
void OnValueChanged(float value)
{
float snapped;
if (TryGetSnapPoint(value, out snapped))
{
slider.onValueChanged.RemoveListener(OnValueChanged);
slider.value = snapped;
slider.onValueChanged.AddListener(OnValueChanged);
}
UpdatePositionDisplay();
}
public void OnPointerUp(PointerEventData eventData)
{
float snapped;
if (!TryGetSnapPoint(slider.value, out snapped)) return;
slider.onValueChanged.RemoveListener(OnValueChanged);
slider.value = snapped;
slider.onValueChanged.AddListener(OnValueChanged);
UpdatePositionDisplay();
}
bool TryGetSnapPoint(float value, out float snapped)
{
snapped = value;
if (snapPoints == null || snapPoints.Length == 0) return false;
float minDist = float.MaxValue;
float nearest = value;
foreach (float point in snapPoints)
{
float dist = Mathf.Abs(value - point);
if (dist < minDist)
{
minDist = dist;
nearest = point;
}
}
if (minDist < snapThreshold)
{
snapped = nearest;
return true;
}
return false;
}
public string GetPositionText(float value)
{
float rounded = Mathf.Round(value * 100f) / 100f;
return rounded.ToString("0.##");
}
void UpdatePositionDisplay()
{
if (positionTMP == null) return;
positionTMP.text = GetPositionText(slider.value);
}
}解説
- snapPoints と snapThreshold
snapPointsは、スナップさせたい位置を正規化値(0〜1)で指定する配列です。Sliderの最小値が0、最大値が1に対応します。例えば 0.5fはスライダーの中央、0.2fは左から20%の位置になります。
snapThresholdは吸着距離です。スライダーの値がスナップポイントからこの値以内に入ると、自動でスナップします。0.02fは全体の2%の範囲です。値を大きくするほど遠くからでも吸着するようになります。
どちらもInspectorから変更できるので、スクリプトを書き直さずに調整できます。
- OnValueChanged でのスナップ処理
ドラッグ中にスナップポイントへ近づいたとき、リアルタイムで吸着させる処理です。
slider.value = snappedを実行するとonValueChangedが再び発火します。そのままではOnValueChangedが無限に呼ばれてしまうため、値を変更する前にRemoveListenerでイベントを一時解除し、変更後にAddListenerで再登録しています。
- IPointerUpHandler でのスナップ処理
IPointerUpHandlerのOnPointerUpは、マウスボタンやタッチを離した瞬間に呼ばれます。
ドラッグを離した瞬間にもスナップ処理を走らせることで、「ドラッグ中はスナップしなかったが、離したときにスナップポイントの近くにいた」というケースも確実に吸着できます。OnValueChangedとの二重対応で、取りこぼしなくスナップが動作します。
IPointerUpHandlerを使うためには、クラス宣言に IPointerUpHandlerを追加し、スクリプトをスライダーと同じGameObjectにアタッチする必要があります。
- TryGetSnapPoint メソッド
スナップすべきかどうかを判定するメソッドです。out引数でスナップ先の値を返します。
処理の流れは以下の通りです。
- すべてのsnapPointsとの距離を計算する
- 最も近いポイントを探す
- その距離がsnapThreshold未満であればスナップ対象と判定しtrueを返す
- 閾値以上であれば何もせずfalseを返す
複数のスナップポイントがあっても、常に最も近い1点に吸着します。
- TextMeshPro での値表示
現在のスライダー値をTextMeshProのテキストに表示します。Mathf.Round で小数点2桁に丸めてから “0.##”フォーマットで表示しています。”0.##” は末尾の不要なゼロを省略してくれるので、0.50ではなく 0.5のようにすっきり表示されます。
positionTMPがnullの場合は何もしないようにしているので、テキスト表示が不要な場合はアタッチしなくても動作します。
実演
スクリプトができたら、Inspectorで以下の通り設定してください。

| Slider | シーン上のSliderコンポーネントをアタッチ |
| PositonTMP | 値表示に使うTextMeshProUGUIをアタッチ |
| SnapPoints | スナップさせたい位置を正規化値(0〜1)で入力 |
| SnapThreshold | 吸着距離。デフォルト `0.02f` から調整可能 |
スクリプトはSliderと同じGameObjectにアタッチしてください。別のGameObjectにアタッチすると `IPointerUpHandler` が機能しません。
まとめ
Unityのスライダーに任意の位置へのスナップ機能を実装しました。
- snapPoints(float配列)でスナップ位置を自由に指定できます
- snapThresholdで吸着距離を調整できます
- onValueChangedと IPointerUpHandlerを併用することで、ドラッグ中・離した瞬間どちらにも対応できます
- – リスナーの二重発火を防ぐため、値変更前後で RemoveListener → AddListenerの順で処理します
音量調整や設定画面など、特定の値に合わせやすくしたいスライダーUIに活用してみてください。



