multi touch / swipe issues

Hi,

I’m making a multiplayer running game on android using C#.
Each player get’s half of the screen and uses his fingers to swipe over the screen to simulate running.

I’ve run into a big wall getting both players to be able to swipe simultaneously.
I’ve been using fingerId and checking where they tap to see which player is swiping where but many different ways haven’t been working out.

What am I doing wrong?

if (Input.touchCount > 0)
    {
        if (Input.touches[Input.touchCount -1].position.x < screenwidth.x)
        {
            if (AActive == false)
            {
                AActive = true;
                touchA = Input.touches[Input.touchCount -1];
                startPosA = Input.touches[Input.touchCount -1].position;
                startTimeA = Time.time;
                fingerA = Input.touches[Input.touchCount - 1].fingerId;
            }
        }
        else
        {
            if (BActive == false)
            {
                BActive = true;
                touchB = Input.touches[Input.touchCount -1];
                startPosB = Input.touches[Input.touchCount -1].position;
                startTimeB = Time.time;
                fingerB = Input.touches[Input.touchCount - 1].fingerId;
            }
        }

switch (touchA.phase)
        {
            case TouchPhase.Began:
                couldBeSwipeA = true;
                //stuff
                break;
            case TouchPhase.Moved:
                //stuff
                break;
            case TouchPhase.Ended:

                endPosA = Input.touches[fingerA].position;

                if (couldBeSwipeA == false)
                {
                    AActive = false;
                }

                if (couldBeSwipeA)
                {
                    swipeDistA = startPosA.x - endPosA.x;
                    swipeTimeA = Time.time - startTimeA;

                    if ((swipeTimeA < maxSwipeTime) && (swipeDistA > minSwipeDist))
                    {
                        //calculate speed
                    }
                }
                AActive = false;
                break;
        }

And the same happens for touchB.phase in a switch.

5 Answers

5

It looks like you’re getting really close. Here are some notes that will hopefully help you finish things off:

-in the foreach, the second if (checking for b touch) should be replaced with just “else”. It’s probably a little more efficient. If you like the self-commenting nature of the existing code, you can add a comment above or under the “else”: else // (touch.position.x > screenWidth.x)

-“AActive” and “BActive” are adding complexity and should probably be removed. Instead, just use the “touchA” and “touchB” variables. You will also need to change your “DoSwipe” method to indicate which touch is being processed, for example: “void DoSwipe(Touch touch, bool isTouchA)”. Then when you call it, pass in “true” when processing touch A, and “false” otherwise.

-By passing in the touch as a bool, you can also remove all position checking in the DoTouch method. This will properly handle the case where a touch starts in the “A” region, and moves to the “B” region (it should still be considered an A touch).

-Make sure you clear out touchA and touchB when the touches end. I suggest initializing these to -1, and then setting them to -1 when their respective touches end.

Finally, Update() needs to be a little different. The idea is to identify existing and new touches and pass them off to DoTouch. Here’s one way to do it:

    // Update is called once per frame
    void Update()
    {
        if (Input.touchCount > 0)
        {
            foreach (Touch touch in Input.touches)
            {
				bool touchIsTouchA = false;
				
				if (touch.fingerId == touchA)
				{
					// Existing touch identified as touch A.
					touchIsTouchA = true;
				}
				else if (touch.fingerId == touchB)
				{
					// Existing touch identified as touch B.
					// Do nothing, as "touchIsTouchA" is already false.
				}
                else if (touch.position.x < screenWidth.x)
                {
					// New touch identified as touch A.
					AStatus = "A is touching";
                    touchA = touch.fingerId;
                    touchIsTouchA = true;
                }
                else
                {
					// New touch identified as touch B.
					// No need to set "touchIsTouchA", as it is already false.
                    BStatus = "B is touching";
                    touchB = touch.fingerId;
                }
				
				DoSwipe(touch, touchIsTouchA);
            }//end foreach
        }//end if touchcount
    }

Give this a shot, and let me know if you have more questions/issues.

-Kevin

Thanks so much for all your time/help. It is solved. Wish I could give you 10 thumbs up :) I kept the AActive and BActive bools in my code as I didn't want the player to be able to swipe with more than 1 finger at the same time. I've reposted my new and complete code if you are interested. Thanks again!

–

Glad to hear it! Good luck with the rest of your game.

–

Oh, also -- your idea about disallowing multiple touches per side is a good one. If you want, you can probably do away with your bools, and just check for "touchA != -1" instead, since this should mirror the bool at all times. In this smallish algorithm, it's easy enough to see what's going on, so it's not a big deal in any case. In a larger design, a good "best practice" would be to use a method instead of a bool, like this: bool IsTouchAActive(){ return touchA != -1; }

–

this is what i use for multitouch its in java but can easily be converted

	for(var i = 0; i <Input.touchCount; ++i){
		var touch = Input.GetTouch(i);

//put code here


}

from this u will wat to use if(touch.position )//is in screen area

–

I’m not sure if I’m supposed to add a new answer (or just comment), but the comment doesn’t seem to allow for code snippets.

It’s odd that you aren’t hitting the “Ended” case for any touches. Try my code below (in context), and see if this fixes it. If not, another alternative would be to process the movement inside the “Moved” case, and just add each movement incrementally (rather than all at once at the end).

Here’s my suggested code in the context of what you’ve written:

// Note that "fingerA" and "fingerB" have to persist across calls (make them members of your class, for instance)
// You'll also need some way to clear them out -- typically it will be in the "Ended" phase, but more experimentation
// may be needed.

foreach (Touch touch in Input.touches)
       {
         if (touch.fingerId == fingerA)
         {
           touchA = touch;
         }
         else if (touch.fingerId == fingerB)
         {
           touchB = touch;
         }
// Code below is mostly identical to yours, other than the "else if" part and the touch variable
         else if (touch.position.x < screenwidth.x)
         {
            if (AActive == false)
            {
                AActive = true;
                touchA = touch;
                startPosA = touch.position;
                startTimeA = Time.time;
                fingerA = touch.fingerId;
            }
         }
         else
         {
            if (BActive == false)
            {
                BActive = true;
                touchB = touch;
                startPosB = touch.position;
                startTimeB = Time.time;
                fingerB = touch.fingerId;
            }
        }

// The switch below is the same.  You should factor it out into a function, though,
// rather than repeating the switch twice; this will reduce bugs in the long run.

The code above should compile, but again it’s tough without actually being able to test it. Let me know if this gets you closer.

There were some errors in my code, which I've fixed. Hopefully this works now.

–

Hi kmeboe, sorry for not replying. I had some personal issues that needed to be dealt with. Your response has helped me a lot in getting my head around everything. But I still haven't got it working (but a lot closer). I rewrote the whole script using a function for the switch. I'll reply to this thread with my whole new code. The obvious problem is with the AActive and BActive bools. If you press on both sides of the screen, both bools are turned on, this leading to when player B swipes, the code recognizes it as a wrong swipe. I have no clue how to write it in any other way though.

–

my new (whole) code after a few revisions:
(I’m testing this on an android device while testing the code.)

using UnityEngine;
using System.Collections;

public class Swiper : MonoBehaviour
{

    Vector2 screenWidth;

    bool AActive = false;
    bool BActive = false;

    int touchA;
    int touchB;

    bool couldBeSwipe;

    public Touch touch;

    //
    string AStatus;
    string BStatus;
    string switchStatus;

    // Use this for initialization
    void Start()
    {
        screenWidth = new Vector2(Screen.width / 2, 0);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.touchCount > 0)
        {
            foreach (Touch touch in Input.touches)
            {
                    if (touch.position.x < screenWidth.x)
                    {
                        AStatus = "A is touching";
                        touchA = touch.fingerId;
                        AActive = true;

                        DoSwipe(touch);

                    }
                    if (touch.position.x > screenWidth.x)
                    {
                        BStatus = "B is touching";
                        touchB = touch.fingerId;
                        BActive = true;

                        DoSwipe(touch);
                    }
            }//end foreach
        }//end if touchcount
    }

    void DoSwipe(Touch touch)
    {
        switch (touch.phase)
        {
            case TouchPhase.Began:
                switchStatus = "BEGAN";
                //save fingerId
                //save startPos
                //save startTime
                couldBeSwipe = true;
                break;
            case TouchPhase.Moved:
                switchStatus = "MOVED";
                if (AActive == true)
                {
                    if (touch.position.x > screenWidth.x)
                    {
                        couldBeSwipe = false;
                        switchStatus = "MOVED-AAA";
                    }
                }

                if (BActive == true)
                {
                    if (touch.position.x < screenWidth.x)
                    {
                        couldBeSwipe = false;
                        switchStatus = "MOVED-BBB";
                    }
                }
                    
                break;
            case TouchPhase.Ended:
                if (couldBeSwipe)
                {
                    switchStatus = "ENDED-CORRECT";
                }
                else
                {
                    switchStatus = "ENDED";
                }

                if (AActive == true) { AActive = false; }
                else if (BActive == true) { BActive = false; }
                
                break;
        }



        if (touch.fingerId == touchA)
        {
            //bereken de switch info als dat van spelerA
        }
        else if (touch.fingerId == touchB)
        {
            //bereken als spelerB
        }

        
    }

    void OnGUI()
    {
        GUI.Box(new Rect(5, 5, 200, 200), "");
        GUI.Box(new Rect(0, 0, screenWidth.x, Screen.height), "");
        GUI.Label(new Rect(10, 10, 180, 180), "AStatus: " + AStatus);
        GUI.Label(new Rect(10, 30, 180, 180), "BStatus: " + BStatus);
        GUI.Label(new Rect(10, 50, 180, 180), "touchA: " + touchA.ToString());
        GUI.Label(new Rect(10, 70, 180, 180), "touchB: " + touchB.ToString());
        GUI.Label(new Rect(10, 90, 180, 180), "switch: " + switchStatus);
        GUI.Label(new Rect(10, 110, 180, 180), "AActive: " + AActive.ToString());
        GUI.Label(new Rect(10, 130, 180, 180), "BActive: " + BActive.ToString());
        GUI.Label(new Rect(10, 150, 180, 180), "touchCount: " + Input.touchCount.ToString());
        //--
        GUI.Label(new Rect(10, 170, 180, 180), "fingerId: " + touch.fingerId.ToString());

    }
}

This is the solution, added AActive and BActive bools so only 1 finger from each player will be taken into the calculation at the same time:

using UnityEngine;
using System.Collections;

public class Swiper : MonoBehaviour
{
    Vector2 screenWidth;

    int touchA;
    int touchB;

    bool AActive;
    bool BActive;

    public Touch touch;

    //
    string AStatus;
    string BStatus;
    string switchStatus;

    // Use this for initialization
    void Start()
    {
        screenWidth = new Vector2(Screen.width / 2, 0);
        touchA = -1;
        touchB = -1;
        AActive = false;
        BActive = false;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.touchCount > 0)
        {
            foreach (Touch touch in Input.touches)
            {
                bool touchIsTouchA = false;
                bool couldBeSwipe = true;

                if (touch.fingerId == touchA)
                {
                    //existing touch is touchA
                    touchIsTouchA = true;
                }
                else if (touch.fingerId == touchB)
                {
                    //do nothing, as touchIsTouchA is already false
                }
                else if (touch.position.x < screenWidth.x && AActive == false)
                {
                    //new touch identified as A
                    AStatus = "A is touching";
                    touchA = touch.fingerId;
                    touchIsTouchA = true;
                    AActive = true;
                }
                else if (touch.position.x > screenWidth.x && BActive == false)
                {
                    //if nothing else, new touch must be B
                    BStatus = "B is touching";
                    touchB = touch.fingerId;
                    BActive = true;
                }
                else
                {
                    couldBeSwipe = false;
                }

                DoSwipe(touch, touchIsTouchA, couldBeSwipe);

            } //end foreach
        } //end if touchcount
    } //end Update

    void DoSwipe(Touch touch, bool isTouchA, bool couldBeSwipe)
    {
        if (couldBeSwipe)
        {
            switch (touch.phase)
            {
                case TouchPhase.Began:
                    switchStatus = "BEGAN";
                    //save fingerId
                    //save startPos
                    //save startTime
                    break;
                case TouchPhase.Moved:
                    switchStatus = "MOVED";
                    if (isTouchA == true)
                    {
                        switchStatus = "MOVED-AAA";
                    }
                    else
                    {
                        switchStatus = "MOVED-BBB";
                    }
                    break;
                case TouchPhase.Ended:

                    if (isTouchA == true)
                    {
                        touchA = -1;
                        switchStatus = "ENDED-AAA";
                        AActive = false;
                        //Make further calculations for player A
                    }
                    else
                    {
                        touchB = -1;
                        switchStatus = "ENDED-BBB";
                        BActive = false;
                        //Make further calculations for player A
                    }
                    break;
            } // end switch
        } //end if      
    } //DoSwipe method