I need to move an object on a grid board, by a standard distance (eg 1 unit horizontally or vertically), depending on whether I swipe one finger horizontally or vertically. The distance is fixed as I mentioned and the direction will depend on whether I move from bottom to top, from left to right or vice versa.
As I understand I will have to use (or at least this could be a way to do it) the Touch.deltaPosition and the tan of the vector that my swipe will follow. This way, if I swipe from bottom to top, the vector that I follow has an angle fo 90degrees (in relation to the xy coordinate system of the board my object moves on) and its tan is infinite.
Using if I will then tell my object to move one unit up if this kind of swipe is input.
The problem is I dont know how to script that, no matter how much I browse through the Script Reference and the Forum. Any hint would be very appreciated.
I actually took andee’s code, transphered it to C#, and improved it a litte:
The major difference in contrast to andee’s script is, that for the swipe to be registered you dont have to wait till your finger leaves the touchscreen, and that you don’t have to swipe "totally horizonta"l (Just the x-difference gets measured → swiping is (way) more convinient).
As of now, this is as i find the way it is implemented in most games/apps nowdays.
//If you’d like to do the same for a vertical swipe, just exchange every “.x” with a “.y” .
Feel free to use
-Toonk
public float minSwipeDist, maxSwipeTime;
bool couldBeSwipe;
IEnumerator checkHorizontalSwipes () //Coroutine, wich gets Started in "Start()" and runs over the whole game to check for swipes
{
while (true) { //Loop. Otherwise we wouldnt check continoulsy ;-)
foreach (Touch touch in Input.touches) { //For every touch in the Input.touches - array...
switch (touch.phase) {
case TouchPhase.Began: //The finger first touched the screen --> It could be(come) a swipe
couldBeSwipe = true;
startPos = touch.position; //Position where the touch started
swipeStartTime = Time.time; //The time it started
break;
case TouchPhase.Stationary: //Is the touch stationary? --> No swipe then!
couldBeSwipe = false;
break;
}
float swipeTime = Time.time - swipeStartTime; //Time the touch stayed at the screen till now.
float swipeDist = Mathf.Abs (touch.position.x - startPos.x); //Swipedistance
if (couldBeSwipe && swipeTime < maxSwipeTime && swipeDist > minSwipeDist) {
// It's a swiiiiiiiiiiiipe!
couldBeSwipe = false; //<-- Otherwise this part would be called over and over again.
if (Mathf.Sign (touch.position.x - startPos.x) == 1f) { //Swipe-direction, either 1 or -1.
//Right-swipe
} else {
//Left-swipe
}
}
}
yield return null;
}
I had an issue with TouchPhase randomly being stationary in what I considered a fast valid swipe. I got around this issue by modifying andee’s code to require stationary to continuously be the phase for 6 frames before it’ll cancel.
Here’s the full code:
public class TouchGesture
{
[System.Serializable]
public class GestureSettings
{
public float minSwipeDist = 100;
public float maxSwipeTime = 10;
}
private GestureSettings settings;
private float swipeStartTime;
private bool couldBeSwipe;
private Vector2 startPos;
private int stationaryForFrames;
private TouchPhase lastPhase;
public TouchGesture(GestureSettings gestureSettings)
{
this.settings = gestureSettings;
}
public IEnumerator CheckHorizontalSwipes(Action onLeftSwipe, Action onRightSwipe) //Coroutine, which gets Started in "Start()" and runs over the whole game to check for swipes
{
while (true)
{ //Loop. Otherwise we wouldnt check continuously ;-)
foreach (Touch touch in Input.touches)
{ //For every touch in the Input.touches - array...
switch (touch.phase)
{
case TouchPhase.Began: //The finger first touched the screen --> It could be(come) a swipe
couldBeSwipe = true;
startPos = touch.position; //Position where the touch started
swipeStartTime = Time.time; //The time it started
stationaryForFrames = 0;
break;
case TouchPhase.Stationary: //Is the touch stationary? --> No swipe then!
if (isContinouslyStationary(frames:6))
couldBeSwipe = false;
break;
case TouchPhase.Ended:
if (isASwipe(touch))
{
couldBeSwipe = false; //<-- Otherwise this part would be called over and over again.
if (Mathf.Sign(touch.position.x - startPos.x) == 1f) //Swipe-direction, either 1 or -1.
onRightSwipe(); //Right-swipe
else
onLeftSwipe(); //Left-swipe
}
break;
}
lastPhase = touch.phase;
}
yield return null;
}
}
private bool isContinouslyStationary(int frames)
{
if (lastPhase == TouchPhase.Stationary)
stationaryForFrames++;
else // reset back to 1
stationaryForFrames = 1;
return stationaryForFrames > frames;
}
private bool isASwipe(Touch touch)
{
float swipeTime = Time.time - swipeStartTime; //Time the touch stayed at the screen till now.
float swipeDist = Mathf.Abs(touch.position.x - startPos.x); //Swipe distance
return couldBeSwipe && swipeTime < settings.maxSwipeTime && swipeDist > settings.minSwipeDist;
}
}
Different sensitivity of TouchPhase.Stationary between devices is likely due to the DPI of the device. A better fix, than what I did here, might be to factor in the DPI to determine how many continuously stationary frames make it no longer a swipe.
Hey thanks for this post, helped me a lot.
I also found it easier to get the gestures from other scripts using a delegate.
public enum SwipeDirection
{
None,
Left,
Right
}
private static SwipeDirection lastSwipe;
public static Action<SwipeDirection> OnSwipeGesture;
------------
// then put this after you get the direction
if (OnSwipeGesture != null)
OnSwipeGesture.Invoke(lastSwipe);
made my life so much easier