using System;
using UnityEngine;
using UnityEngine.Events;
namespace KhawajjaGames.Mobile
{
///
/// Multi-touch Mobile Gesture & Swipe Controller for Unity Android and iOS.
/// Handles 4-directional/8-directional swipes, taps, double-taps, and pinch-to-zoom.
/// Created by Khawajja Games Studio (https://khawajjagames.com)
///
public class MobileSwipeController : MonoBehaviour
{
public enum SwipeDirection { None, Left, Right, Up, Down, UpLeft, UpRight, DownLeft, DownRight }
[Header("Swipe Thresholds")]
[Tooltip("Minimum pixel distance required to register a swipe gesture")]
public float minSwipeDistance = 50f;
[Tooltip("Maximum duration in seconds for a quick flick swipe")]
public float maxSwipeTime = 0.5f;
[Header("Tap Settings")]
public float doubleTapMaxDelay = 0.3f;
[Header("Events")]
public UnityEvent OnSwipeDetected;
public UnityEvent OnTap;
public UnityEvent OnDoubleTap;
public UnityEvent OnPinchZoom; // Returns zoom delta
private Vector2 _fingerDownPosition;
private Vector2 _fingerUpPosition;
private float _fingerDownTime;
private float _lastTapTime;
private float _initialPinchDistance;
private void Update()
{
HandleTouchInput();
}
private void HandleTouchInput()
{
// Single Touch Handling (Tap, Double Tap, Swipe)
if (Input.touchCount == 1)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
_fingerDownPosition = touch.position;
_fingerUpPosition = touch.position;
_fingerDownTime = Time.time;
}
else if (touch.phase == TouchPhase.Moved)
{
_fingerUpPosition = touch.position;
}
else if (touch.phase == TouchPhase.Ended)
{
_fingerUpPosition = touch.position;
float duration = Time.time - _fingerDownTime;
float distance = Vector2.Distance(_fingerDownPosition, _fingerUpPosition);
if (distance >= minSwipeDistance && duration <= maxSwipeTime)
{
DetectSwipeDirection();
}
else if (distance < minSwipeDistance)
{
DetectTap();
}
}
}
// Dual Touch Handling (Pinch to Zoom)
else if (Input.touchCount == 2)
{
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
OnPinchZoom?.Invoke(deltaMagnitudeDiff);
}
}
private void DetectSwipeDirection()
{
Vector2 direction = _fingerUpPosition - _fingerDownPosition;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
if (angle < 0) angle += 360f;
SwipeDirection swipe = SwipeDirection.None;
if (angle >= 337.5f || angle < 22.5f) swipe = SwipeDirection.Right;
else if (angle >= 22.5f && angle < 67.5f) swipe = SwipeDirection.UpRight;
else if (angle >= 67.5f && angle < 112.5f) swipe = SwipeDirection.Up;
else if (angle >= 112.5f && angle < 157.5f) swipe = SwipeDirection.UpLeft;
else if (angle >= 157.5f && angle < 202.5f) swipe = SwipeDirection.Left;
else if (angle >= 202.5f && angle < 247.5f) swipe = SwipeDirection.DownLeft;
else if (angle >= 247.5f && angle < 292.5f) swipe = SwipeDirection.Down;
else if (angle >= 292.5f && angle < 337.5f) swipe = SwipeDirection.DownRight;
OnSwipeDetected?.Invoke(swipe);
}
private void DetectTap()
{
float timeSinceLastTap = Time.time - _lastTapTime;
if (timeSinceLastTap <= doubleTapMaxDelay)
{
OnDoubleTap?.Invoke();
_lastTapTime = 0f;
}
else
{
OnTap?.Invoke();
_lastTapTime = Time.time;
}
}
}
}