Detect swipe gesture direction

I searched for the same thing, and found it reasonable to create asset for easy swipe detection, and share it with community. So here it is on github. My solution supports different usecases, including: 8-directions swipe detection, 4-directions, 2-directions (left-right or up-down), swipes on hexagonal grid. All listed is included as a presets, but also you can configure it to detect any number of Vector3 directions. So ti`s really flexible. Also, you can try WebGL build or see video tutorial. If you try it, please, let me know (via youtube comment, or see Contacts section on github), was it suitable for your case, and was it comfortable enough.


Modified Developper's approach for a more precise controller (and less code! =D ) :

using System;
using UnityEngine;
using UnityEngine.Events;
using Utilities;

public class SwipeManager : MonoBehaviour {

  public float swipeThreshold = 40f;
  public float timeThreshold = 0.3f;

  public UnityEvent onSwipeLeft;
  public UnityEvent onSwipeRight;
  public UnityEvent onSwipeUp;
  public UnityEvent onSwipeDown;

  private Vector2 _fingerDown;
  private DateTime _fingerDownTime;
  private Vector2 _fingerUp;
  private DateTime _fingerUpTime;

  private void Update () {
    if (Input.GetMouseButtonDown(0)) {
      _fingerDown = Input.mousePosition;
      _fingerUp = Input.mousePosition;
      _fingerDownTime = DateTime.Now;
    }

    if (Input.GetMouseButtonUp(0)) {
      _fingerDown = Input.mousePosition;
      _fingerUpTime = DateTime.Now;
      CheckSwipe();
    }

    foreach (var touch in Input.touches) {
      if (touch.phase == TouchPhase.Began) {
        _fingerDown = touch.position;
        _fingerUp = touch.position;
        _fingerDownTime = DateTime.Now;
      }

      if (touch.phase == TouchPhase.Ended) {
        _fingerDown = touch.position;
        _fingerUpTime = DateTime.Now;
        CheckSwipe();
      }
    }
  }

  private void CheckSwipe() {
    var duration = (float)_fingerUpTime.Subtract(_fingerDownTime).TotalSeconds;
    var dirVector = _fingerUp - _fingerDown;

    if (duration > timeThreshold) return;
    if (dirVector.magnitude < swipeThreshold) return;

    var direction = dirVector.Rotation(180f).Round();

    print(direction);

    if (direction >= 45 && direction < 135) onSwipeUp.Invoke();
    else if (direction >= 135 && direction < 225) onSwipeRight.Invoke();
    else if (direction >= 225 && direction < 315) onSwipeDown.Invoke();
    else if (direction >= 315 && direction < 360 || direction >= 0 && direction < 45) onSwipeLeft.Invoke();
  }
}

I can spot some few problems in your code. It's not a good idea to compare Vector3 with == or !=. Approximate comparison is fine. You are using Input.GetMouseButtonDown on a mobile platform.

You need to use Input.touches to do this. Loop over it, store the beginning position in TouchPhase.Began and then the end position in TouchPhase.Ended. You can then use both variables to figure it which direction the finger went.

The code below detects swipe direction even when the finger is not yet released with the help of TouchPhase.Moved. You can disable that by enabling the detectSwipeOnlyAfterRelease boolean variable. You can also modify SWIPE_THRESHOLD for sensitivity.

public class SwipeDetector : MonoBehaviour
{
    private Vector2 fingerDown;
    private Vector2 fingerUp;
    public bool detectSwipeOnlyAfterRelease = false;

    public float SWIPE_THRESHOLD = 20f;

    // Update is called once per frame
    void Update()
    {

        foreach (Touch touch in Input.touches)
        {
            if (touch.phase == TouchPhase.Began)
            {
                fingerUp = touch.position;
                fingerDown = touch.position;
            }

            //Detects Swipe while finger is still moving
            if (touch.phase == TouchPhase.Moved)
            {
                if (!detectSwipeOnlyAfterRelease)
                {
                    fingerDown = touch.position;
                    checkSwipe();
                }
            }

            //Detects swipe after finger is released
            if (touch.phase == TouchPhase.Ended)
            {
                fingerDown = touch.position;
                checkSwipe();
            }
        }
    }

    void checkSwipe()
    {
        //Check if Vertical swipe
        if (verticalMove() > SWIPE_THRESHOLD && verticalMove() > horizontalValMove())
        {
            //Debug.Log("Vertical");
            if (fingerDown.y - fingerUp.y > 0)//up swipe
            {
                OnSwipeUp();
            }
            else if (fingerDown.y - fingerUp.y < 0)//Down swipe
            {
                OnSwipeDown();
            }
            fingerUp = fingerDown;
        }

        //Check if Horizontal swipe
        else if (horizontalValMove() > SWIPE_THRESHOLD && horizontalValMove() > verticalMove())
        {
            //Debug.Log("Horizontal");
            if (fingerDown.x - fingerUp.x > 0)//Right swipe
            {
                OnSwipeRight();
            }
            else if (fingerDown.x - fingerUp.x < 0)//Left swipe
            {
                OnSwipeLeft();
            }
            fingerUp = fingerDown;
        }

        //No Movement at-all
        else
        {
            //Debug.Log("No Swipe!");
        }
    }

    float verticalMove()
    {
        return Mathf.Abs(fingerDown.y - fingerUp.y);
    }

    float horizontalValMove()
    {
        return Mathf.Abs(fingerDown.x - fingerUp.x);
    }

    //////////////////////////////////CALLBACK FUNCTIONS/////////////////////////////
    void OnSwipeUp()
    {
        Debug.Log("Swipe UP");
    }

    void OnSwipeDown()
    {
        Debug.Log("Swipe Down");
    }

    void OnSwipeLeft()
    {
        Debug.Log("Swipe Left");
    }

    void OnSwipeRight()
    {
        Debug.Log("Swipe Right");
    }
}

Thanks to Programmer, I used his suggestion and wrote a small component which works both with mouse and touch. The mouse will allow you to debug app on PC. I also added time threshold in seconds, since swipe cannot be too long.

using System;
using UnityEngine;
using UnityEngine.Events;

public class SwipeManager : MonoBehaviour {

  public float swipeThreshold = 50f;
  public float timeThreshold = 0.3f;

  public UnityEvent OnSwipeLeft;
  public UnityEvent OnSwipeRight;
  public UnityEvent OnSwipeUp;
  public UnityEvent OnSwipeDown;

  private Vector2 fingerDown;
  private DateTime fingerDownTime;
  private Vector2 fingerUp;
  private DateTime fingerUpTime;

  private void Update () {
    if (Input.GetMouseButtonDown(0)) {
      this.fingerDown = Input.mousePosition;
      this.fingerUp = Input.mousePosition;
      this.fingerDownTime = DateTime.Now;
    }
    if (Input.GetMouseButtonUp(0)) {
      this.fingerDown = Input.mousePosition;
      this.fingerUpTime = DateTime.Now;
      this.CheckSwipe();
    }
    foreach (Touch touch in Input.touches) {
      if (touch.phase == TouchPhase.Began) {
        this.fingerDown = touch.position;
        this.fingerUp = touch.position;
        this.fingerDownTime = DateTime.Now;
      }
      if (touch.phase == TouchPhase.Ended) {
        this.fingerDown = touch.position;
        this.fingerUpTime = DateTime.Now;
        this.CheckSwipe();
      }
    }
  }

  private void CheckSwipe() {
    float duration = (float)this.fingerUpTime.Subtract(this.fingerDownTime).TotalSeconds;
    if (duration > this.timeThreshold) return;

    float deltaX = this.fingerDown.x - this.fingerUp.x;
    if (Mathf.Abs(deltaX) > this.swipeThreshold) {
      if (deltaX > 0) {
        this.OnSwipeRight.Invoke();
        //Debug.Log("right");
      } else if (deltaX < 0) {
        this.OnSwipeLeft.Invoke();
        //Debug.Log("left");
      }
    }

    float deltaY = fingerDown.y - fingerUp.y;
    if (Mathf.Abs(deltaY) > this.swipeThreshold) {
      if (deltaY > 0) {
        this.OnSwipeUp.Invoke();
        //Debug.Log("up");
      } else if (deltaY < 0) {
        this.OnSwipeDown.Invoke();
        //Debug.Log("down");
      }
    }

    this.fingerUp = this.fingerDown;
  }
}

Tags:

C#

Unity3D