Search Unity

Handling multiple swipes?

Discussion in 'Scripting' started by Serge144, Jul 26, 2021.

  1. Serge144

    Serge144

    Joined:
    Oct 2, 2018
    Posts:
    65
    So I have this simple script that allows me to rotate a Cannon vertically or horizontally using the Input.Touch system.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Swiper : MonoBehaviour
    4. {
    5.     private Touch initTouch = new Touch();
    6.  
    7.     private float rotX = 0f;
    8.     private float rotY = 0f;
    9.     private Vector3 origRot;
    10.  
    11.     public float rotSpeed = 0.2f;
    12.  
    13.     private float MinRotationY = -90f;
    14.     private float MaxRotationY = 90f;
    15.  
    16.     private float MinRotationX = -55f;
    17.     private float MaxRotationX = 0f;
    18.  
    19.     void Start()
    20.     {
    21.         origRot = transform.eulerAngles;
    22.         rotX = origRot.x;
    23.         rotY = origRot.y;
    24.     }
    25.  
    26.     private void FixedUpdate()
    27.     {
    28.         foreach (Touch touch in Input.touches) {
    29.             if (touch.phase == TouchPhase.Began)
    30.             {
    31.                 initTouch = touch;
    32.             }
    33.             else if (touch.phase == TouchPhase.Moved)
    34.             {
    35.                 float deltaX = touch.position.x - initTouch.position.x;
    36.                 float deltaY = touch.position.y - initTouch.position.y;
    37.                 rotX += deltaX * Time.deltaTime * rotSpeed;
    38.                 rotX = Mathf.Clamp(rotX, MinRotationY, MaxRotationY); // horizontal rotation of the cannon is around the Y axis.
    39.                 rotY += deltaY * Time.deltaTime * rotSpeed * -1f;
    40.                 rotY = Mathf.Clamp(rotY, MinRotationX, MaxRotationX); // vertical rotation of the cannon is around the X axis.
    41.                 transform.eulerAngles = new Vector3(rotY, rotX, 0f); // In portrait mode, swiping vertically = horizontal swipe in landscape mode. So rotY = x and rotX = y;
    42.             }
    43.             else if (touch.phase == TouchPhase.Ended)
    44.             {
    45.                 initTouch = new Touch();
    46.             }
    47.         }
    48.     }
    49. }
    50.  
    The problem is that, if you start swiping with one finger, and then start swiping with another finger (2 touches) the cannon has a very weird snappy behaviour. The desired behaviour is for the cannon to simply rotate normally. This fast 2 finger swipes may occur because it is a way for the player to swipe faster (alternating between 2 thumbs for example, but of course the "alternating" swipe will tend to overlap with each other, for example:
    thumb 1:S----------E~~~~~~~~S-----------E
    thumb 2:~~~~~~S------------E
    S = Start of swipe
    E = End of swipe

    As you can see, the first swipe of the thumb 2, overlaps with the first of the thumb 1.

    How can I then improve this script?

    Thanks in advance.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    The problem is you are not tracking the
    fingerId
    of the first finger, so the second one comes in, which might end up in slot zero, and you track that. There's no guarantee of order of that list of touches, but the
    fingerId
    is consistent from finger to finger over the life of the swipe.

    https://docs.unity3d.com/ScriptReference/Touch-fingerId.html

    Broadly you need to "grab" a particular fingerId and ONLY pay attention that one until it is released.

    If you want to see my general-purpose "virtual analog button" example, check out the VAButton class (supported by the MicroTouch class) in my proximity_buttons project.

    proximity_buttons is presently hosted at these locations:

    https://bitbucket.org/kurtdekker/proximity_buttons

    https://github.com/kurtdekker/proximity_buttons

    https://gitlab.com/kurtdekker/proximity_buttons

    https://sourceforge.net/projects/proximity-buttons/

    EDIT: you also MUST process input like this in Update(), not FixedUpdate()!!! If you do it in FixedUpdate() you will miss touch events.
     
    Serge144 likes this.
  3. Serge144

    Serge144

    Joined:
    Oct 2, 2018
    Posts:
    65
    Thank you, I made this small change to my script and is working nicely. I'll leave it here for anyone who needs it. Basically I added a Dictionary where the Key is the fingerID and the Value is the Initial touch. Adding a new Key-Value pair everytime the touch is in Began phase. Then i'm fetching that initial touch on the Moved phase. Finally I remove it from the Dictionary on the Ended phase.

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class Swiper : MonoBehaviour
    5. {
    6.     private float RotX = 0f;
    7.     private float RotY = 0f;
    8.     private Vector3 OrigRot;
    9.  
    10.     public float RotSpeed = 0.2f;
    11.  
    12.     private float MinRotationY = -90f;
    13.     private float MaxRotationY = 90f;
    14.     private float MinRotationX = -55f;
    15.     private float MaxRotationX = 0f;
    16.  
    17.     Dictionary<int, Touch> touches = new Dictionary<int, Touch>(); // the touch fingerID (key) and the initial touch value.
    18.    
    19.     void Start()
    20.     {
    21.         OrigRot = transform.eulerAngles;
    22.         RotX = OrigRot.x;
    23.         RotY = OrigRot.y;
    24.     }
    25.  
    26.     private void Update()
    27.     {
    28.         foreach (Touch touch in Input.touches) {
    29.             if (touch.phase == TouchPhase.Began)
    30.             {
    31.                 if(!touches.ContainsKey(touch.fingerId))
    32.                     touches.Add(touch.fingerId, touch);
    33.             }
    34.             else if (touch.phase == TouchPhase.Moved)
    35.             {
    36.                 Touch initialTouch = touches[touch.fingerId];
    37.                 float deltaX = touch.position.x - initialTouch.position.x;
    38.                 float deltaY = touch.position.y - initialTouch.position.y;
    39.                 RotX += deltaX * Time.deltaTime * RotSpeed;
    40.                 RotX = Mathf.Clamp(RotX, MinRotationY, MaxRotationY); // horizontal rotation of the cannon is around the Y axis.
    41.                 RotY += deltaY * Time.deltaTime * RotSpeed * -1f;
    42.                 RotY = Mathf.Clamp(RotY, MinRotationX, MaxRotationX); // vertical rotation of the cannon is around the X axis.
    43.                 transform.eulerAngles = new Vector3(RotY, RotX, 0f); // In portrait mode, swiping vertically = horizontal swipe in landscape mode. So rotY = x and rotX = y;
    44.             }
    45.             else if (touch.phase == TouchPhase.Ended)
    46.             {
    47.                 touches.Remove(touch.fingerId);
    48.             }
    49.         }
    50.     }
    51. }
    52.  
     
    Kurt-Dekker likes this.