TouchPhase.Ended/Began missed?

Wow, alot of issues today, so here is my code:

    void Update(){// ON PC UPDATE MUST BE COMMENTED OUT
		if (Input.GetTouch(0).phase == TouchPhase.Began) {
		   ray = _main.ScreenPointToRay(Input.GetTouch(0).position);
           if (Physics.Raycast(ray, out hit, 100,1<<gameObject.layer)){//hit on the very same object
			TouchDown();
		   }		
		}else if (Input.GetTouch(0).phase == TouchPhase.Ended||Input.GetTouch(0).phase == TouchPhase.Canceled) {
			TouchUp();
		}
}

When deployed to Android everything works as expected, but when I release a finger than quickly tap on another location TouchUp doesn’t get called like the old finger is still acting though I was expecting TouchUp would fire uo and another Touch sequence begins with index 0, now I don’t know what speed at Ended would be missed at but is it that I maybe tapped another finger at less than 30ms or so between taps that Update couldn’t keep track of the new tap and Unity took it fo the old one?
I tried to use fingerId but I don’t quite get how they work nor how unity indexes touches, the docs say just:

Touch.fingerId
var fingerId: int;
Description
The unique index for touch.

No potential values no example no rule of thub, WTF?

???

So, a few observations:

  1. Use platform conditional code to avoid having to comment out functions, see Unity - Manual: Conditional compilation

  2. Every time a new touch happens, the touch object gets a unique index. I don’t know exactly what these indices look like, but perhaps it’s just an incrementing number. This means that you can track touch objects and don’t need to rely on the position in the array. So, the documentation is pretty good. Each touch gets a unique index. Maybe Debug.Log the index and you’ll quickly get the idea.

  3. You could keep a track of the touch index. If the “new” touch index differs from the last touch index, then you have a new touch, and possibly the Ended has been missed (but I doubt it, see below.)

  4. You really should assume that you’ll get multiple touches. In one frame you might have a new touch event starting and an old one ending. This could mean you have 2 TouchEvents even though you only have a single finger down. So, use Input.touchCount, as the last two examples on the GetTouch page show.

HTH

Sorry for the thread necro, but I just ran into the head-line bug when testing touch on WebGL in Safari on an iPad Air 3.
After considerable testing I can verify that when multi-touch has one Moving touch, the other touch will frequently fail to report an Ended or Canceled phase. (e.g. left touch = movement/strafing, right touch = turning).
My solution is to store the fingerId for both the move and the turn when they are ‘Began’. If we get any phase except Canceled/Ended then set a boolean true for that input.
After processing all touches, check the bools, if either is false, call the TouchEnd method for that id.

        bool moveInput = false, turnInput = false;
        for (int i = 0, l = Input.touchCount; i < l; i++)
        {
            Touch t = Input.GetTouch(i);
            Debug.Log($"touch {i} {t.phase}");
            switch (t.phase)
            {
                case TouchPhase.Began:
                    TouchStart(t);
                    break;
                case TouchPhase.Moved:
                    TouchMove(t);
                    break;
                //case TouchPhase.Canceled:
                //case TouchPhase.Ended:
                //    TouchEnd(t.fingerId);
                //    break;
            }

            if (t.phase != TouchPhase.Ended && t.phase != TouchPhase.Canceled)
            {
                if (t.fingerId == moveId) moveInput = true;
                if (t.fingerId == turnId) turnInput = true;
            }
        }

        if (moveInput == false && moveId != -1)
        {
            TouchEnd(moveId);
            moveId = -1;
        }
        if (turnInput == false && turnId != -1)
        {
            TouchEnd(turnId);
            turnId = -1;
        }
1 Like