Im creating a touch manager so that i can track multi touches on the iphone more easily, but i have a quick question.
Can i count on the fact that a touch will always have a Began/Ended no matter the frame rate. Will there ever be an instance where it jumps straight to Stationary/Moved/Ended without going through Began or will there be an instance where a touch will never reported as Ended?
I know the unity remove is not a good source for simulation of the touch, so i know it skips some of these phases but does unity act the same way on the phone at low frame rates.
iPhoneTouchPhase.Canceled is the last phase of a touch; this is called if the touch input is undefined (like happens when a user answers a call with their face)
I assume unity is simply wrapping the sdk methods, so they are probably plagued with the same event “shuffling” as well
Yeah… well this makes things a bit more complicated…
I think i might be able to code something up to handled dropped phases… like if i hit a Moved before i hit a Began then treat the current phase as Began instead or if i don’t see an Ended but the touch is no longer active, build a dummy Ended touch and process that.
I created this FingerManager.cs script to handle multiple touches, moves, ends and cancels. Just drop it on only one master object and listen to the FingerBegin, FingerMove, FingerEnd and FingerCancel on all objects you want to be interactive. The target objects need a collider.
using UnityEngine;
using System.Collections;
public class Finger {
public iPhoneTouch touch;
public bool moved = false;
public ArrayList colliders = new ArrayList();
}
public class FingerManager : MonoBehaviour {
public ArrayList fingers = new ArrayList();
void Update () {
RaycastHit[] hits;
foreach (iPhoneTouch evt in iPhoneInput.touches) {
if (evt.phase==iPhoneTouchPhase.Began) {
Finger finger = new Finger();
finger.touch = evt;
Ray ray = Camera.main.ScreenPointToRay(evt.position);
hits = Physics.RaycastAll(ray);
foreach (RaycastHit hit in hits) {
finger.colliders.Add(hit.collider);
GameObject to = hit.collider.gameObject;
to.SendMessage("FingerBegin",evt,SendMessageOptions.DontRequireReceiver);
}
fingers.Add(finger);
}
else if (evt.phase==iPhoneTouchPhase.Moved) {
for (int i=0;i<fingers.Count;++i) {
Finger finger = (Finger)fingers[i];
if (finger.touch.fingerId==evt.fingerId) {
finger.moved = true;
foreach (Collider collider in finger.colliders) {
if (collider==null) {continue;}
GameObject to = collider.gameObject;
to.SendMessage("FingerMove",evt,SendMessageOptions.DontRequireReceiver);
}
}
}
}
else if (evt.phase==iPhoneTouchPhase.Ended || evt.phase==iPhoneTouchPhase.Canceled) {
Ray ray = Camera.main.ScreenPointToRay(evt.position);
hits = Physics.RaycastAll(ray);
for (int i=0;i<fingers.Count;) {
Finger finger = (Finger)fingers[i];
if (finger.touch.fingerId==evt.fingerId) {
foreach (Collider collider in finger.colliders) {
if (collider==null) {continue;}
bool canceled = true;
foreach (RaycastHit hit in hits) {
if (hit.collider==collider) {
canceled = false;
GameObject to = collider.gameObject;
to.SendMessage("FingerEnd",evt,SendMessageOptions.DontRequireReceiver);
}
}
if (canceled) {
GameObject to = collider.gameObject;
to.SendMessage("FingerCancel",evt,SendMessageOptions.DontRequireReceiver);
}
}
fingers[i] = fingers[fingers.Count-1];
fingers.RemoveAt(fingers.Count-1);
}
else {
++i;
}
}
}
}
}
}
Write message handlers like this, for your interactive objects.
If you want maximal preciseness, handle touches in FixedUpdate function, not in OnGUI.
And I can share a little bug, we’re fixing now - after 250-300 continuous (both multiple and single) touches you may see 1-2 more TouchEnded states then TouchStarted. Closer to 400 touches, delta may reach 5-6 touches.
That sheds some light on a problem that I have intermittently with “long” touches.
When could we expect this fix to be integrated into a release? Also, I assume there is no real workaround that I could implement before then; or is there something I could do bridge the gap until the next Unity update?
I’m using rozgos FingerManager. Thank you for this! It works very well!
But with a problem in my case. I have several interactive objects in one scene, all with colliders. All using scripts with the Method “FingerEnd” included. The Problem: If at least one object is covered by another one and RaycastAll hits both objects, FingerEnd is called on both objects. With FingerEnd on the last object called first. Might be useful in many cases but I think of at least the same amount of cases there this shouldn’t happen :-). I might be able to find a solution but as I’m a designer this will end up in designers code fo sure… . Reversing the array would be the first part of the solution but I would also like to limit the touch to the object in front. Maybe even keeping the FingerManager flexible for both situations?!
I will give it a try but I 've also attached a simple testscene for the case anyone else is willing.
I’m wondering how I might use this script to handle the sliding of the screen from left to right? I normally just say on first touch, grab the transform.position, then ending touch, grabbing the transform.position, and if second is greater than first, or vise versa, I know the swipe movement direction and can move the camera in the direction accordingly.