- Home /
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
Answer by gebnar · Jan 11, 2012 at 02:31 PM
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.csusing 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.csusing 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[i] = touchList[i]; } //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); }
}
oh man, you are awesome. this is an awesome solution, far better than the POS one I had hacked together. if I could give you $$anonymous$$ORE thumbs up, I absolutely would man.
Answer by gebnar · Nov 17, 2011 at 01:28 PM
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; }
Answer by Maddogc · Dec 08, 2012 at 03:34 PM
First of all MAAAN your solution is awesome :D 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[i] = _TouchList[i];
}
//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);
}
}
Thanks @$$anonymous$$addogc for the contribution to this answer! $$anonymous$$uch appreciated, however you did forget to include the first part of touchhandler.cs, the part that declares "FakeTouch" - so after "using System.Collections.Generic;" and before "public class TouchHandler : $$anonymous$$onoBehavior" you must include this part:
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;
}
}