Swipe Input Controller
Detects touch swipes in four directions with configurable thresholds and dead zones. Fires UnityEvents for each swipe direction with speed and distance tracking.
How to Use
Create an empty GameObject named SwipeManager
Attach the SwipeInputController script to it
Set the minimum swipe distance threshold in the inspector
Wire up the onSwipeUp/Down/Left/Right UnityEvents to your game logic
Test on a mobile device or use mouse input in the editor
Features
- 4-directional swipe detection
- Configurable swipe threshold
- Swipe speed and distance tracking
- Events for swipe start/end/direction
- Dead zone filtering
When to Use This
Built for mobile games that use swipe gestures — card games, puzzle games, endless runners with lane switching. Essential for any touch-based game that needs directional input beyond simple taps.
Common Mistakes
Test on actual devices, not just the Unity Remote — touch behavior differs significantly. The minSwipeDistance threshold needs tuning per device DPI; what feels natural on a phone may be too sensitive on a tablet. Don't forget to handle multi-touch if your game uses simultaneous gestures.
Source Code
using UnityEngine;
using UnityEngine.Events;
public class SwipeInputController : MonoBehaviour
{
[Header("Swipe Settings")]
[SerializeField] private float minSwipeDistance = 50f;
[SerializeField] private float maxSwipeTime = 0.5f;
[SerializeField] private float deadZone = 20f;
[Header("Events")]
public UnityEvent onSwipeUp;
public UnityEvent onSwipeDown;
public UnityEvent onSwipeLeft;
public UnityEvent onSwipeRight;
public UnityEvent<Vector2> onSwipeStart;
public UnityEvent<Vector2, float, float> onSwipeEnd; // direction, speed, distance
public enum SwipeDirection { None, Up, Down, Left, Right }
public SwipeDirection LastSwipeDirection { get; private set; }
public float LastSwipeSpeed { get; private set; }
public float LastSwipeDistance { get; private set; }
public bool IsSwiping { get; private set; }
private Vector2 touchStartPos;
private float touchStartTime;
private bool tracking;
private void Update()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
HandleTouch(touch.phase, touch.position);
}
else
{
// Mouse fallback for editor testing
if (Input.GetMouseButtonDown(0))
HandleTouch(TouchPhase.Began, Input.mousePosition);
else if (Input.GetMouseButton(0))
HandleTouch(TouchPhase.Moved, Input.mousePosition);
else if (Input.GetMouseButtonUp(0))
HandleTouch(TouchPhase.Ended, Input.mousePosition);
}
}
private void HandleTouch(TouchPhase phase, Vector2 position)
{
switch (phase)
{
case TouchPhase.Began:
touchStartPos = position;
touchStartTime = Time.time;
tracking = true;
IsSwiping = true;
onSwipeStart?.Invoke(position);
break;
case TouchPhase.Ended:
case TouchPhase.Canceled:
if (!tracking) return;
tracking = false;
IsSwiping = false;
ProcessSwipe(position);
break;
}
}
private void ProcessSwipe(Vector2 endPos)
{
float elapsed = Time.time - touchStartTime;
if (elapsed > maxSwipeTime) return;
Vector2 delta = endPos - touchStartPos;
float distance = delta.magnitude;
if (distance < minSwipeDistance) return;
float speed = distance / Mathf.Max(elapsed, 0.001f);
LastSwipeDistance = distance;
LastSwipeSpeed = speed;
SwipeDirection direction = GetSwipeDirection(delta);
LastSwipeDirection = direction;
onSwipeEnd?.Invoke(delta.normalized, speed, distance);
switch (direction)
{
case SwipeDirection.Up: onSwipeUp?.Invoke(); break;
case SwipeDirection.Down: onSwipeDown?.Invoke(); break;
case SwipeDirection.Left: onSwipeLeft?.Invoke(); break;
case SwipeDirection.Right: onSwipeRight?.Invoke(); break;
}
}
private SwipeDirection GetSwipeDirection(Vector2 delta)
{
if (Mathf.Abs(delta.x) < deadZone && Mathf.Abs(delta.y) < deadZone)
return SwipeDirection.None;
if (Mathf.Abs(delta.x) > Mathf.Abs(delta.y))
{
return delta.x > 0 ? SwipeDirection.Right : SwipeDirection.Left;
}
else
{
return delta.y > 0 ? SwipeDirection.Up : SwipeDirection.Down;
}
}
public void ResetSwipe()
{
LastSwipeDirection = SwipeDirection.None;
LastSwipeSpeed = 0f;
LastSwipeDistance = 0f;
tracking = false;
IsSwiping = false;
}
}