- Home /
Multitouch: Second touch interferes with the first.
I am trying to create a control system for touch devices. Touching the left side of the screen moves the cameras position and the right side of the screen moves the camera angle (much like fps controls). The problem comes with multitouch. For some reason, the moment I touch my second finger, it overwrites the first fingers origin.
Here is the current code in c#
private Rect leftBox = new Rect(0, 0, Screen.width / 2, Screen.height);
private Vector2 leftOrigin = new Vector2();
private Vector2 rightOrigin = new Vector2();
private float dragSpeed = 2;
private string side = "left";
private void touchMovementManager()
{
if (Input.touchCount > 0)
{
foreach (Touch touch in Input.touches)
{
if (touch.phase == TouchPhase.Began)
{
// add origin for this touch to array
if (leftBox.Contains(touch.position))
{
leftOrigin = touch.position;
side = "left";
}
else
{
rightOrigin = touch.position;
side = "right";
}
}
if (touch.phase != TouchPhase.Ended && touch.phase != TouchPhase.Canceled)
{
switch (side)
{
case "left":
Vector3 pos = Camera.main.ScreenToViewportPoint(touch.position - leftOrigin);
Vector3 move = Camera.main.transform.TransformDirection(pos);
Vector3 newMove = new Vector3(move.x, -move.z, move.y);
transform.Translate(newMove, Space.World);
break;
case "right":
Camera.mainCamera.transform.Rotate(Vector3.up * (touch.position.x - rightOrigin.x) / (dragSpeed * 100));
Camera.mainCamera.transform.Rotate(Vector3.left * (touch.position.y - rightOrigin.y) / (dragSpeed * 100));
break;
default:
break;
}
}
}
}
}
So I'll try explain as best I can as what is meant to be happening. For every touch, if the touch has just began, it stores the origin (depending on if its the left side of the screen or right side). On the next frame, it then alters movement by acceleration based on how far the finger has moved from the origin.
Heres where it breaks: When a second finger is added to the other side of the screen, it changes the movement to whatever side the second finger has touched but it accelerates rapidly as the first finger is far away from the origin.
What I am confused about is that because the entire statement is wrapped in a foreach, it should be executing this seperately for each touch (finger), but for some reason they are getting muddled.
Be aware that touchMovementManager() is called on the Update() loop.
I had this exact same problem. http://answers.unity3d.com/questions/257505/touchdeltaposition-works-differently-with-two-fing.html
Now unfortunately I have not found a good answer, however in my research there are a few things to consider. How your device implements touch specifically multi touch can make a big difference (this is an inherent issue with android devices, haven't messed with iOS as much). The second is that the touch index/ID and the data for the touch itself are not necessarily correlated. $$anonymous$$eaning the first iteration of your loop will not always correspond to data from the same finger. (This drove me insane!) I dont know if Unity 4 has fixed or addressed this but in 3.5 it was killing me, especially when trying to implement my own pinch zoom. You can make it work if you are willing to implement some filtering and also infer which finger is touching based on last movements etc. but it is work.
Hey guys, got it all working perfectly with how I wanted it. I'm going to upload the code here for learning purposes. I'm not happy with the code itself, its something I hacked in very quickly (especially the rotation stuff), but I found very little documentation for what I expect should be quite a popular control system. Thanks to Bunny who helped me sort out the fingerID stuff.
The code:
http://pastie.org/private/eb6i0e7isnk4xkwlz9ra
Should work if you add it to the main camera along with a prefab.
Controls:
Swipe the left side of the screen to move the position. Swipe the right side of the screen to change the rotation. Tap on a cube to trigger a raycast(I was experimenting with being able to interact with objects while using this movement system).
Answer by Bunny83 · Dec 12, 2012 at 01:07 AM
Your "side" variable does exist only once. Each touch can and will set it and overrides the value the first touch has set.
You should use two variables and not a boolean but an int!
Each Touch has a fingerId this fingerId doesn't change until the touch is ended. This id is the only thing which you can use to identify a certain finger. So you have a move finger and a rotate finger, you need a variable for each one. Now when a touch happens in the "move area" you set the moveFingerID to touch.fingerId. When a touch happens in the "rotate area" You set the rotateFingerID to touch.fingerId.
Now you can identify each finger and assign each to the right actions.
In addition you should "clear" the fingerId when it's touch ended / canceled.
Something like that:
private Rect leftBox = new Rect(0, 0, Screen.width / 2, Screen.height);
private Vector2 positionRef = new Vector2();
private int positionFingerId = -1;
private Vector2 rotationRef = new Vector2();
private int rotationFingerId = -1;
private float dragSpeed = 2;
private void touchMovementManager()
{
foreach (Touch touch in Input.touches)
{
if (touch.phase == TouchPhase.Began)
{
// add origin for this touch to array
if (leftBox.Contains(touch.position))
{
positionRef = touch.position;
positionFingerId = touch.fingerId;
}
else
{
rotationRef = touch.position;
rotationFingerId = touch.fingerId;
}
}
if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
{
if (touch.fingerId == positionFingerId)
positionFingerId = -1;
if (touch.fingerId == rotationFingerId)
rotationFingerId = -1;
}
else
{
if (touch.fingerId == positionFingerId)
{
Vector3 pos = Camera.main.ScreenToViewportPoint(touch.position - positionRef);
Vector3 move = Camera.main.transform.TransformDirection(pos);
Vector3 newMove = new Vector3(move.x, -move.z, move.y);
transform.Translate(newMove, Space.World);
}
if (touch.fingerId == rotationFingerId)
{
Camera.mainCamera.transform.Rotate(Vector3.up * (touch.position.x - rotationRef.x) / (dragSpeed * 100));
Camera.mainCamera.transform.Rotate(Vector3.left * (touch.position.y - rotationRef.y) / (dragSpeed * 100));
}
}
}
}
ps, i changed your script here in the editor, so no guarantee that it compiles right away :D I renamed your left and right variables since you should use names which are bound to their function. I would also rename the "leftBox" and probably create another one for the rotation. If you decide to flip the controls (or if you make it configurable for the user) the "left" is very confusing.