TouchPhase not firing correctly - Android

On my Android device, everything seem to work correctly the first time I load up my app, but if I switch to another application and back, the TouchPhase.began, and TouchPhase.ended phases simply do not fire most of the time, which makes my game unplayable.

Does anyone have a suggestion for how I can fix this problem, or how I can get around it?

Here’s the code I’ve been using to track if the user touches a GUITexture:

function Update(){
	for(var i : int = 0; i < Input.touchCount; i++){
		var touch : Touch = Input.GetTouch(i);
		if(touch.phase == TouchPhase.Began && guiTexture.HitTest(touch.position)){
			DoSomething();
		}
	}
}

Any help would be MUCH appreciated! Many thanks in advance if you can solve this problem for me. =D

OK… I’ve come up with a second solution for this problem, this time on a more global scale. Essentially, I have provided a replacement for the Input.touches array using a static class and a single script that can be attached to any GameObject in the scene.
Here are the scripts:

First, the static class, you don’t need an instance of this class anywhere. Just have it in your project.

`


Touches.cs

using UnityEngine;
using System.Collections;

public static class Touches : object {
static public FakeTouch touches;
static public int touchCount;

static public FakeTouch GetTouch(int index){
	if(index < touches.Length){
		return touches[index];
	}else{
		return new FakeTouch();
	}
}

}

`

Second, the script. Attach this to any object in each scene in which you want to access the alternative touches array.

`


TouchHandler.cs

using UnityEngine;
using System.Collections;

//public enum TouchState {Began,Stationary,Moved,Ended}

public struct FakeTouch {
public int fingerId;
public Vector2 position;
public Vector2 deltaPosition;
public float deltaTime;
public int tapCount;
public TouchPhase phase;
public Touch realTouch;

public FakeTouch(int id, Vector2 pos, Vector2 dPos, float dTime, int taps, TouchPhase tPhase, Touch touch){
	fingerId = id;
	position = pos;
	deltaPosition = dPos;
	deltaTime = dTime;
	tapCount = taps;
	phase = tPhase;
	realTouch = touch;
}

}

public class TouchHandler : MonoBehaviour {
// public GUIText gText;

void Start() {
	Touches.touches = new FakeTouch[0];
	Touches.touchCount = 0;
}

void Update(){
	FakeTouch[] oldTouches = Touches.touches;
	Touch[] newTouches = Input.touches;
	bool[] usedTouches = new bool[newTouches.Length];
	FakeTouch[] touchList = new FakeTouch[100]; //initialize new Touch array
	int index = 0; //new touch array index
	
	//first iterate through old touches, and update any changed values
	for(int ot = 0; ot < oldTouches.Length; ot++){
		//any touch with phase ended can be ignored
		if(oldTouches[ot].phase != TouchPhase.Ended){
			//this touch needs to be added to the array
			bool updated = false;
			//iterate through new touches, checking for same ID
			for(int nt = 0; nt < newTouches.Length; nt++){
				if(oldTouches[ot].fingerId == newTouches[nt].fingerId){
					//id's are a match, update old touch and add it to array
					touchList[index] = UpdateTouch(oldTouches[ot],newTouches[nt]);
					index++;
					//keep track of newTouches that we already added into the array
					usedTouches[nt] = true;
					//we found the matching touch, no need to keep checking stuff
					updated = true;
					break;
				}
			}
			if(!updated){
				//if there was no corresponding new touch, add this touch to the array as Ending
				touchList[index] = EndTouch(oldTouches[ot]);
				index++;
			}
		}
	}//finished iterating through old touches
	//now iterate through new touches
	for(int nt = 0; nt < newTouches.Length; nt++){
		//check if this touch was already used
		if(!usedTouches[nt]){
			//touch hasn't been added yet
			touchList[index] = StartTouch(newTouches[nt]);
			index++;
		}
	}//finished iterating through new touches
	//now put all collected touches into a small array
	Touches.touches = new FakeTouch[index];
	for(int i = 0; i < index; i++){
		Touches.touches _= touchList*;*_

* }*
* //set touchCount*
* Touches.touchCount = index;*

* }*

* private FakeTouch StartTouch(Touch newTouch){*
* return new FakeTouch(*
_ /fingerId/newTouch.fingerId,
/position/newTouch.position,
/deltaPosition/newTouch.deltaPosition,
/deltaTime/newTouch.deltaTime,
/tapCount/newTouch.tapCount,
/phase/TouchPhase.Began,
/realTouch/newTouch);_

* }*

* private FakeTouch UpdateTouch(FakeTouch oldTouch, Touch newTouch){*
* TouchPhase phase;*
* if(oldTouch.position == newTouch.position){*
* phase = TouchPhase.Stationary;*
* }else{*
* phase = TouchPhase.Moved;*
* }*
* return new FakeTouch(*
_ /fingerId/newTouch.fingerId,
/position/newTouch.position,
/deltaPosition/newTouch.deltaPosition,
/deltaTime/newTouch.deltaTime,
/tapCount/newTouch.tapCount,
/phase/phase,
/realTouch/newTouch);_

* }*

* private FakeTouch EndTouch(FakeTouch oldTouch){*
* return new FakeTouch(*
_ /fingerId/oldTouch.fingerId,
/position/oldTouch.position,
/deltaPosition/oldTouch.deltaPosition,
/deltaTime/oldTouch.deltaTime,
/tapCount/oldTouch.tapCount,
/phase/TouchPhase.Ended,
/realTouch/oldTouch.realTouch);_

* }*
}

`

In case anyone else runs into this problem, I’ve come up with the following code tidbit that lets me track TouchPhases in my own way, and has an easy-to-access event handler section.

Still, If anyone has an actual solution to the basic problem of TouchPhases not firing correctly, I would like to see a proper answer posted here.

private var fingerLatched:boolean;
private var latchedId:int;
private var latchedPosition:Vector2;
private var button:GUITexture;
private var touchState:TouchState;

enum TouchState {
	Idle = 0,
	Began = 1,
	Stationary = 2,
	Moving = 3,
	Ended = 4,
}

//custom variables
//end custom variables

function TouchHandler(){
	//custom event handler
	if(touchState == TouchState.Began){
		DoSomething();
	}
	//end custom event Handler
}

function Start(){
	//custom start code
	//end custom start code
	button = GetComponent( GUITexture );
	fingerLatched = false;
	latchedId = -1;
	touchState = TouchState.Idle;
	latchedPosition = Vector2.zero;
}

function Update(){
	//custom update code
	//end custom update code
	touchState = TouchState.Idle;
	for(var i:int = 0; i < Input.touchCount; i++){
		var touch:Touch = Input.GetTouch(i);
		var touchPos:Vector2 = touch.position;
		
		if( button.HitTest(touch.position) ){
			//button is being touched
			if(fingerLatched){
				//finger is latched
				if(touch.fingerId == latchedId){
					//being touched by same finger
					if(touch.position == latchedPosition){
						//touch is stationary
						touchState = TouchState.Stationary;
					}else{
						//touch is moving
						touchState = TouchState.Moving;
						//update latched position for next frame
						latchedPosition = touch.position;
					}
				}else{
					//being touched by extra finger
				}
			}else{
				//no finger is latched
				latchFinger(touch);
				touchState = TouchState.Began;
			}
		}
	}

	if(touchState == TouchState.Idle && fingerLatched){
		//touch just ended (latched finger either left the button, or stopped
		//touching the screen)
		unLatch();
		touchState = TouchState.Ended;
	}
	
	if(touchState != TouchState.Idle){
		//button is being touched, or touch just ended...
		TouchHandler();
	}
}

function latchFinger(touch:Touch){
	//latch the current finger
	fingerLatched = true;
	latchedId = touch.fingerId;
	latchedPosition = touch.position;
}

function unLatch(){
	//release the latch
	fingerLatched = false;
	latchedId = -1;
	latchedPosition = Vector2.zero;
}

First of all MAAAN your solution is awesome :smiley: thnx a lot!!!
I ran Unity’s Profiler and found out that the array allocations at start of Update
have a hard impact on GC. So I cached them on Lists and GC is now fine.
And here is the appropriate modification of your solution:

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

public class TouchHandler : MonoBehaviour 
{ 
	private int _Index;	
	
	private List<bool> _UsedTouchesList 	= new List<bool>();
	private List<FakeTouch> _OldTouchesList = new List<FakeTouch>();
	private List<FakeTouch> _TouchList		= new List<FakeTouch>();
	private List<Touch> _NewTouchesList		= new List<Touch>();
	
	private FakeTouch[] _InitTouchArray;
	
	void Start() 
	{
	    Touches.touches = new FakeTouch[0];
	    Touches.touchCount = 0;
		
		_InitTouchArray = new FakeTouch[100];
	}
	
	void Update()
	{
		ClearLists();

		_OldTouchesList.AddRange(Touches.touches);
	    _NewTouchesList.AddRange(Input.touches);
		_UsedTouchesList.AddRange(new bool[_NewTouchesList.Count]);
	    _TouchList.AddRange(_InitTouchArray); //initialize new Touch array with preinitialized array
		_Index = 0; //new touch array index	
		
	    //first iterate through old touches, and update any changed values
	    for(int ot = 0; ot < _OldTouchesList.Count; ot++)
		{
	        //any touch with phase ended can be ignored
	       	if(_OldTouchesList[ot].phase != TouchPhase.Ended)
			{
				//this touch needs to be added to the array
				bool updated = false;
	         
	         	//iterate through new touches, checking for same ID
				for(int nt = 0; nt < _NewTouchesList.Count; nt++)
				{    
					if(_OldTouchesList[ot].fingerId == _NewTouchesList[nt].fingerId)
					{
						//id's are a match, update old touch and add it to array
						_TouchList[_Index] = UpdateTouch(_OldTouchesList[ot],_NewTouchesList[nt]);
						_Index++;						
			             
						//keep track of _NewTouchesList that we already added into the array
			            _UsedTouchesList[nt] = true;
			            //we found the matching touch, no need to keep checking stuff
			            updated = true;
			            break;	          
					}	         
				}
	         				
				if(!updated)
				{
		          //if there was no corresponding new touch, add this touch to the array as Ending
		          _TouchList[_Index] = EndTouch(_OldTouchesList[ot]);
		          _Index++;	        	 
				}
			}
	    }//finished iterating through old touches
		
	    //now iterate through new touches
	    for(int nt = 0; nt < _NewTouchesList.Count; nt++)
		{
	       	//check if this touch was already used	       
			if(!_UsedTouchesList[nt])
			{
	         //touch hasn't been added yet
	         _TouchList[_Index] = StartTouch(_NewTouchesList[nt]);
	         _Index++;
			}
	    }//finished iterating through new touches
	   
		//now put all collected touches into a small array
	    Touches.touches = new FakeTouch[_Index];
		
	    for(int i = 0; i < _Index; i++)
		{
	       Touches.touches <em>= _TouchList*;*</em>

* }*

* //set touchCount*
* Touches.touchCount = Index;
_
}*

* private void ClearLists ()*
* {*
* _OldTouchesList.Clear();
_NewTouchesList.Clear();
_UsedTouchesList.Clear();
TouchList.Clear();
_
}*

* private FakeTouch StartTouch(Touch newTouch)*
* {*
* return new FakeTouch(*
/fingerId/newTouch.fingerId,
/position/newTouch.position,
/deltaPosition/newTouch.deltaPosition,
/deltaTime/newTouch.deltaTime,
/tapCount/newTouch.tapCount,
/phase/TouchPhase.Began,
/realTouch/newTouch);
* }*

* private FakeTouch UpdateTouch(FakeTouch oldTouch, Touch newTouch)*
* {*
* TouchPhase phase;*

* if(oldTouch.position == newTouch.position)*
* {*
* phase = TouchPhase.Stationary;*
* }*
* else*
* {*
* phase = TouchPhase.Moved;*
* }*

* return new FakeTouch(*
_ /fingerId/newTouch.fingerId,
/position/newTouch.position,
/deltaPosition/newTouch.deltaPosition,
/deltaTime/newTouch.deltaTime,
/tapCount/newTouch.tapCount,
/phase/phase,
/realTouch/newTouch);_

* }*

* private FakeTouch EndTouch(FakeTouch oldTouch)*
* {*
* return new FakeTouch(*
/fingerId/oldTouch.fingerId,
/position/oldTouch.position,
/deltaPosition/oldTouch.deltaPosition,
/deltaTime/oldTouch.deltaTime,
/tapCount/oldTouch.tapCount,
/phase/TouchPhase.Ended,
/realTouch/oldTouch.realTouch);
* }*
}