Unity Input Systemでマウス・タッチ共通のドラッグ操作を実装する
スマホ向けゲームで「PCではドラッグできるのに、実機では操作が不安定」という問題を避けるため、Pointer入力を1本化します。Unity 6ではInput System 1.17が利用できます。
方針
- マウスとタッチをPointerとして扱う
- 押した瞬間にはドラッグを開始しない
- 一定距離を超えたらドラッグ状態へ移行する
- 離した位置に応じてスナップまたは元に戻す
Input Actions
Pointを<Pointer>/position、Pressを<Pointer>/pressに割り当てます。
実装例
using UnityEngine;
using UnityEngine.InputSystem;
public sealed class PointerDragController : MonoBehaviour
{
[SerializeField] private InputActionReference pointAction;
[SerializeField] private InputActionReference pressAction;
[SerializeField] private Camera targetCamera;
[SerializeField] private float dragThreshold = 12f;
private Vector2 pressScreenPosition;
private Vector3 startWorldPosition;
private bool pressed;
private bool dragging;
private void OnEnable()
{
pointAction.action.Enable();
pressAction.action.Enable();
pressAction.action.performed += OnPress;
pressAction.action.canceled += OnRelease;
}
private void OnDisable()
{
pressAction.action.performed -= OnPress;
pressAction.action.canceled -= OnRelease;
pointAction.action.Disable();
pressAction.action.Disable();
}
private void Update()
{
if (!pressed) return;
Vector2 current = pointAction.action.ReadValue<Vector2>();
if (!dragging &&
Vector2.Distance(pressScreenPosition, current) >= dragThreshold)
{
dragging = true;
}
if (!dragging) return;
Vector3 world = ScreenToWorld(current);
transform.position = new Vector3(
world.x,
world.y,
startWorldPosition.z
);
}
private void OnPress(InputAction.CallbackContext context)
{
pressed = true;
dragging = false;
pressScreenPosition = pointAction.action.ReadValue<Vector2>();
startWorldPosition = transform.position;
}
private void OnRelease(InputAction.CallbackContext context)
{
if (dragging)
{
TrySnapOrReturn();
}
pressed = false;
dragging = false;
}
private Vector3 ScreenToWorld(Vector2 screen)
{
Vector3 p = new(screen.x, screen.y,
Mathf.Abs(targetCamera.transform.position.z -
transform.position.z));
return targetCamera.ScreenToWorldPoint(p);
}
private void TrySnapOrReturn()
{
Collider2D hit = Physics2D.OverlapPoint(transform.position);
if (hit != null && hit.TryGetComponent(out SnapTarget target))
{
transform.position = target.SnapPosition;
return;
}
transform.position = startWorldPosition;
}
}
スナップ先は小さなコンポーネントに分けます。
using UnityEngine;
public sealed class SnapTarget : MonoBehaviour
{
public Vector3 SnapPosition => transform.position;
}
誤操作を減らすポイント
-
dragThresholdを入れる - UI上のPointerをゲーム側に通さない
- スナップ判定に余裕を持たせる
- 成功時に音や小さなアニメーションを返す
- 端末ごとにSafe Areaと解像度を確認する
まとめ
入力デバイスごとに処理を分けるより、Pointer単位で状態遷移を作ると管理しやすくなります。
「押す→一定距離動く→ドラッグ→離す」を明示すると、クリックとドラッグの誤判定も減らせます。
公式ドキュメント: https://docs.unity3d.com/ja/current/Manual/com.unity.inputsystem.html
長洲港町電気工事では、ゲーム・アプリ制作にも取り組んでいます。