- Home /
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.
Answer by kmeboe · Aug 30, 2012 at 05:51 PM
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!
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" ins$$anonymous$$d, 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 ins$$anonymous$$d of a bool, like this:
bool IsTouchAActive(){ return touchA != -1; }
Answer by reptilebeats · Aug 20, 2012 at 11:17 AM
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
Answer by kmeboe · Aug 20, 2012 at 05:53 PM
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.
I'm somewhat new to gesture handling, but I have some suggestions to try:
I believe "position.x < screenwidth.x" will always be true. Give "screenwidth.x / 2" a shot ins$$anonymous$$d.
It's possible that "touchA", grabbed in one frame, may not be updated automatically in the next frame. I'm not sure about this, but it's something to consider. One thing you could do is loop on Input.touches (using a foreach, for instance), and if each touch's fingerID maps to a given fingerID, you could grab that touch. $$anonymous$$g.:
foreach (Touch touch in Input.touches) { if (touch.fingerId == fingerA) { touchA = touch; } else if (touch.fingerId == fingerB) { touchB = touch; } //else register new touch for A or B, depending on x location }
The rest of the code seems fine to me, but it's always tough to say without having access to the project. Hopefully these ideas will help, or maybe spark another idea for you. I'll be gone for the next few days, but I'll check back afterwards.
Hi kmeboe,
thanks for your reply.
the var screenwidth actually refers to a var that calculates half the screen(should've said that in 1st post).
I've been experimenting with your foreach statement but haven't had much succes. I'm not entirely clear where in the code I should place it. Before the switch or actually have the switches in each of the if statements within the foreach.
One big problem I'm having is that my code doesn't seem to be continueing to the third case in the switch: ended. It cannot recognize a swipe anymore. Also swiping from side A to side B results in a swipe on side A because when the swipe goes into the other side of the screen he recognizes that as a new touch.
I'm still really stuck on the logic on how to get these two swipes identified so I can call and check on them separately.
Hi kmeboe,
thanks for your reply.
the var screenwidth actually refers to a var that calculates half the screen(should've said that in 1st post).
I've been experimenting with your foreach statement but haven't had much succes. I'm not entirely clear where in the code I should place it. Before the switch or actually have the switches in each of the if statements within the foreach.
One big problem I'm having is that my code doesn't seem to be continueing to the third case in the switch: ended. It cannot recognize a swipe anymore. Also swiping from side A to side B results in a swipe on side A because when the swipe goes into the other side of the screen he recognizes that as a new touch.
I'm still really stuck on the logic on how to get these two swipes identified so I can call and check on them separately.
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.
Answer by NGKrush1 · Aug 30, 2012 at 01:36 PM
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());
}
}
Answer by NGKrush1 · Aug 31, 2012 at 03:05 PM
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
Your answer
Follow this Question
Related Questions
Diagonal swipe issues on Android 1 Answer
Detect if finger lifted off screen 1 Answer
GUITextures refuse to be touched 1 Answer
Make sphere shoot to Touch.position error 1 Answer
Using GUI buttons to move a sprite (C#) 2 Answers