0

I'm implementing specific type of touch controller. The player needs to hold their finger on screen in order to move. Without lifting the finger, the player can swipe in different direction to change direction while still moving. Once the finger is lifted, the player stops moving.

It's been a hard time isolating specific swipes (i.e. lines drawn on screen) ignoring any other movements that are not intended to draw a line. For instance slight finger movements done when the player's finger is "stationary", they mess up my algorithm.

I thought about different approaches such as storing the last few touches and evaluate upon them to determine if there has been a swipe or not, but couldn't implement it properly.

Here's what I've tried so far. Most of the time it works fine, but often the player does erratic movement and goes absolutely the opposite direction to what I intended.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TouchInputHandler : AbstractInputHandler {

    private const int SWIPE_MIN_DISTANCE = 35;

    private Vector2 touchStartPoint;

    private Vector2 touchMovePoint;

    void Update () {
        Touch[] touches = Input.touches;
        if (touches.Length == 1) {
            Touch firstTouch = touches [0];
            if (firstTouch.phase == TouchPhase.Began) {
                this.touchStartPoint = firstTouch.position;
                fireNextDirectionChanged (currentDirection);
            } else if (firstTouch.phase == TouchPhase.Moved) {
                this.touchMovePoint = firstTouch.position;
                if (Vector2.Distance(touchStartPoint, touchMovePoint) > SWIPE_MIN_DISTANCE) {
                    detectSwipeDirection ();
                }
            } else if (firstTouch.phase == TouchPhase.Stationary) {
                touchStartPoint.x = touchMovePoint.x;
                touchStartPoint.y = touchMovePoint.y;
            } else if (firstTouch.phase == TouchPhase.Ended) {
                fireNextDirectionChanged (Constants.Direction.NONE);
            }
        }
    }

    private void detectSwipeDirection() {
        float xDiff = touchMovePoint.x - touchStartPoint.x;
        float yDiff = touchMovePoint.y - touchStartPoint.y;
        Constants.Direction nextDirection;
        bool yGreater = Mathf.Abs(yDiff) >= Mathf.Abs(xDiff);
        if (yGreater) {
            // direction is up or down
            nextDirection = yDiff < 0 ? Constants.Direction.DOWN : Constants.Direction.UP;
        } else {
            // direction is left or right
            nextDirection = xDiff < 0 ? Constants.Direction.LEFT : Constants.Direction.RIGHT;
        }

        if (nextDirection != this.currentDirection)
        {
            fireNextDirectionChanged (nextDirection);
            this.currentDirection = nextDirection;
        }
    }
}
derHugo
  • 83,094
  • 9
  • 75
  • 115
Martin Asenov
  • 1,288
  • 2
  • 20
  • 38
  • @Programmer, how is this question duplicate? did you read the question – Martin Asenov Jun 28 '17 at 11:03
  • Both question says "Detect Swipe". As for "without lifting the finger", check the answer. It has `detectSwipeOnlyAfterRelease` variable that is set to false. Please tell me, what does not make this a duplicate? – Programmer Jun 28 '17 at 13:07
  • @Programmer yes you're right, I read the answer there, modified it a bit for my needs and solved my problem. Do I post my solution and accept it or we close this as duplicate? – Martin Asenov Jun 30 '17 at 21:07
  • It's already closed but you can put your own answer. Also link to the original code you modified. – Programmer Jun 30 '17 at 22:51

1 Answers1

0

I think you simply forgot a line setting the previous touch position to the current one in the TouchPhase.Moved block. Just add touchStartPoint = this.touchMovePoint; and it should work (only tested using mouse inputs but logic remains the same).

Also I commented the TouchPhase.Stationary block: I feel like it does what I suggested before but only when the finger is completely immobile. The final code looks like this:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TouchInputHandler : AbstractInputHandler
{

    private const int SWIPE_MIN_DISTANCE = 35;

    private Vector2 touchStartPoint;

    private Vector2 touchMovePoint;

    void Update()
    {
        Touch[] touches = Input.touches;
        if(touches.Length == 1)
        {
            Touch firstTouch = touches[0];
            if(firstTouch.phase == TouchPhase.Began)
            {
                this.touchStartPoint = firstTouch.position;
                this.currentDirection = Constants.Direction.NONE;
                fireNextDirectionChanged(currentDirection);
            }
            else if(firstTouch.phase == TouchPhase.Moved)
            {
                this.touchMovePoint = firstTouch.position;
                if(Vector2.Distance(touchStartPoint, touchMovePoint) > SWIPE_MIN_DISTANCE)
                {
                    detectSwipeDirection();
                }
                touchStartPoint = this.touchMovePoint; // <= NEW !
            }
            //else if(firstTouch.phase == TouchPhase.Stationary)
            //{
            //    touchStartPoint.x = touchMovePoint.x;
            //    touchStartPoint.y = touchMovePoint.y;
            //}
            else if(firstTouch.phase == TouchPhase.Ended)
            {
                this.currentDirection = Constants.Direction.NONE;
                fireNextDirectionChanged(Constants.Direction.NONE);
            }
        }
    }

    private void detectSwipeDirection()
    {
        float xDiff = touchMovePoint.x - touchStartPoint.x;
        float yDiff = touchMovePoint.y - touchStartPoint.y;
        Constants.Direction nextDirection;
        bool yGreater = Mathf.Abs(yDiff) >= Mathf.Abs(xDiff);
        if(yGreater)
        {
            // direction is up or down
            nextDirection = yDiff < 0 ? Constants.Direction.DOWN : Constants.Direction.UP;
        }
        else
        {
            // direction is left or right
            nextDirection = xDiff < 0 ? Constants.Direction.LEFT : Constants.Direction.RIGHT;
        }

        if(nextDirection != this.currentDirection)
        {
            fireNextDirectionChanged(nextDirection);
            this.currentDirection = nextDirection;
        }
    }
}

Also when using a distance based on in game distance to detect swipe, I'd suggest adding a Screen.width/height ratio somewhere if you want to port your project to multiple resolutions devices (on Start() do something like SWIPE_MIN_DISTANCE *= Screen.width / BASE_SCREEN_WIDTH with private const int BASE_SCREEN_WIDTH = 1024;).

Hope this helps,

Kardux
  • 2,117
  • 1
  • 18
  • 20
  • hey man, tried it and rarely made my player able to move let alone change direction. something is not right with the code. have you tested it? – Martin Asenov Jun 30 '17 at 20:25
  • _"It should work (only tested using mouse inputs but logic remains the same)."_ : of course I tested it before posting. Only difference is I used `Input.GetMouseButton/Up/Down(0)` instead of `firstTouch.phase == TouchPhase.Began/Moved/Ended`. – Kardux Jul 01 '17 at 14:08