TouchPhase.Ended Not Executing

Guys,

I’ve been struggling with simple control I’m writing for my 2D Game, this is how the control look like so you can get a reference of what I’m doing:

1489215--83035--$Control.png

The control works fine but once in a while if I press left and right too fast while jumping what happens is that my character keeps walking in one direction so it moveLeft or moveRight never get set back to false which happens in TouchPhase.Ended. See my code below:

I would love some feedback I have two days stuck on this :frowning: thanks guys!!!

if (Input.touchCount > 0) {

                var touchCount = Input.touchCount;

                for (var i = 0; i < touchCount; i++) {

                    var t = Input.GetTouch (i);

                    if (t.phase == TouchPhase.Began) {

                        if (guiLeft.HitTest (t.position, Camera.main))

                            moveLeft = true;

                        if (guiRight.HitTest (t.position, Camera.main))

                            moveRight = true;

                        if (guiJump.HitTest (t.position, Camera.main))

                        {

                            Debug.Log("Jump pressed");

                            if (groundHit   !stickmanIsDead) {

                                anim.SetBool ("Ground", false);

                                rigidbody2D.AddForce (new Vector2 (0, jumpForce));

                            }

                            //Record start time only once

                            holdJumpDownStartedAt = Time.time;

                        }

                    }

                    //Fall from Platforms

                    if(t.phase == TouchPhase.Stationary){

                        if (guiJump.HitTest (t.position, Camera.main)){

                            if(Time.time - holdJumpDownStartedAt > dropPlatformAt){

                                Debug.Log(string.Format("Holded for {0} second :) ",dropPlatformAt.ToString()));

                                holdJumpDownStartedAt = 0.0f;

                                

                                #region Falling Keystroke

                                var playerFalling = Physics2D.Linecast (transform.position, groundCheck.position, 1 << LayerMask.NameToLayer ("Floor-Air"));

                                //Fall Key

                                if (playerFalling)

                                    StartCoroutine (Fall ());

                                #endregion

                            }

                        }

                    }

                    if (t.phase == TouchPhase.Ended) {

                        if (guiLeft.HitTest (t.position, Camera.main))

                            moveLeft = false;

                        if (guiRight.HitTest (t.position, Camera.main))

                            moveRight = false;

                    }

                }

            }

I was able to pin point the issue. So what happens is as I press right to move my character in the right direction and then move left without lifting my finger, TouchPhase.Ended never executes for my Right Arrow Texture which is true, but how can I detect that in code?

You could try something setting the other move boolean to false when you get the TouchBegan event.

It works a bit better :slight_smile: but still when pressing jump couple of times and left and right fast it gets to a point when moveLeft or moveRight do not get set to false.

ok here’s my solution, I hate to post the entire code but it took me so much time that I think many would benefit from it:

void TouchManagerV2 (){
		//iOS Controls (same as Android because they both deal with screen touches)
		if(Input.touchCount > 0){
			var touchCount = Input.touchCount;
			for (var i = 0; i < touchCount; i++) {
				var touch = Input.GetTouch (i);
				//if player presses less than 1/5 of the screen, go left.
				if(touch.position.x < Screen.width / 5  touch.position.y < Screen.height / 3){
					Debug.Log("Touch left" + rigidbody2D.velocity.x.ToString());
					if(rigidbody2D.velocity.x > 0){
						moveLeft = false;
					}
					if(rigidbody2D.velocity.x > -0.1){
						moveLeft = true;
					}
					moveRight = false;
				}
				//if player presses between 1/5 and 2/5 of the screen, go right.
				if(touch.position.x > Screen.width/5  touch.position.x < Screen.width/5*2  touch.position.y < Screen.height/3){
					Debug.Log("Touch Right" + rigidbody2D.velocity.x.ToString());
					if(rigidbody2D.velocity.x < 0){
						moveRight = false;
					}
					if(rigidbody2D.velocity.x < 0.1){
						moveRight = true;
					}
					moveLeft = false;
				}
			}
		}
		else {
			moveRight = moveLeft = false;
		}

		if(Input.touchCount > 0){
			var touchCount = Input.touchCount;
			for (var i = 0; i < touchCount; i++) {
				var touch = Input.GetTouch (i);

				//2nd touch for jump button
				if(touch.position.x > Screen.width/2  touch.position.y < Screen.height/3){

					if (groundHit   !stickmanIsDead) {
						anim.SetBool ("Ground", false);
						rigidbody2D.AddForce (new Vector2 (0, jumpForce));
					}
					//Record start time only once
					holdJumpDownStartedAt = Time.time;
				}
			}
		}
	}

How are you “checking” to see if this works? Are you testing to a build on a device, or using remote?


From your last thread on this topic, I tried out your code, and played around with the states and how the fire.

The problem is that you are looping through the all the touches in the same place. But each touch has a unique id that is assigned in order the touch occurs. What can happen is that since you are checking them all in the loop, and don’t actually determine if a touch is related to a string of particular event’s you won’t (always) be checking the correct touch for the ended.

So to check for touch.Ended, you have to ensure that you are actually checking on the touch that started it. Which now may be gone from that loop, or replaced by another. A quick solution that I tried that worked out well, was to have a touch variable for each button/touch. If found just store it there, and check it there. That way if the index changes, the touch object will still be accurate.

Something along these lines:

	Touch touchLeft, touchRight, touchJump;
	...
	if (guiLeft.HitTest (Input.GetTouch(i).position, Camera.main)) 
	{
		touchLeft = Input.GetTouch(i);
	}
	etc...

 
    if (touchLeft.phase == TouchPhase.Began) { moveLeft = true; }

    if (touchLeft.phase == TouchPhase.Ended) { moveLeft = false; }

Inside the main loop. Not using touchcount index.

Alternately, you could it with storing the touches by finderId, or even just attaching scripts to each object that allows them to check/store touch details themselves, so there is no mixing up ids/collision.

Heh, well that is one way to approach it. Pretty specific to that one need, but if works it works, and in the end that is what matters. :wink:

Ended up making some more adjustments and yes I’m building to my iphone 5 everytime :slight_smile:

Try my new code:

void CharacterTouchManager (){

		//Debug.Log("Moving..." + rigidbody2D.velocity.x.ToString());
		//Debug.Log("Player is: " + Globals.playIsDoing.ToString());

		//Movement 
		if(Input.touchCount > 0){
			var touchCount = Input.touchCount;
			for (var i = 0; i < touchCount; i++) {
				var touch = Input.GetTouch (i);
				//if player presses less than 1/5 of the screen, go left.
				if(touch.position.x < Screen.width / 5  touch.position.y < Screen.height / 3){
					touchControlSwitchToJumpMode = false;
					//if(rigidbody2D.velocity.x > -0.1){
					//	moveLeft = true;
					//}
					//else 
					//	moveLeft = true;

					moveLeft = true;
					moveRight = false;
				}
				//if player presses between 1/5 and 2/5 of the screen, go right.
				if(touch.position.x > Screen.width / 5  touch.position.x < Screen.width / 5 * 2  touch.position.y < Screen.height / 3){
					touchControlSwitchToJumpMode = false;

					//if(rigidbody2D.velocity.x < 0.1){
					//	moveRight = true;
					//}
					moveRight = true;
					moveLeft = false;
				}
			}
		}
		else {
			moveLeft = moveRight = false;
		}

		//Jumping and Dropping from platforms
		if (Input.touchCount > 0) {
			var touchCount = Input.touchCount;
			for (var i = 0; i < touchCount; i++) {
				var touch = Input.GetTouch (i);
				//Make sure we're not touching jump by itself to avoid character running continously
				if(!touchControlSwitchToJumpMode  touchCount == 1  guiJump.HitTest (touch.position, Camera.main)){
					moveRight = moveLeft = false;
					touchControlSwitchToJumpMode = true;
				}
				if (touch.phase == TouchPhase.Began) {
					if (guiJump.HitTest (touch.position, Camera.main))
					{
						if (groundHit   !stickmanIsDead) {
							anim.SetBool ("Ground", false);
							rigidbody2D.AddForce (new Vector2 (0, jumpForce));
						}
						//Record start time only once
						holdJumpDownStartedAt = Time.time;
					}
				}
				if(touch.phase == TouchPhase.Stationary){
					if (guiJump.HitTest (touch.position, Camera.main)){
						if(Time.time - holdJumpDownStartedAt > dropPlatformAt){
							holdJumpDownStartedAt = 0.0f;
							#region Falling Keystroke
							var playerFalling = Physics2D.Linecast (transform.position, groundCheck.position, 1 << LayerMask.NameToLayer ("Floor-Air"));
							//Fall Key
							if (playerFalling)
								StartCoroutine (Fall ());
							#endregion
						}
					}
				}
			}
		}
	}

Create a separate script and put the following script in the Update(). The code itself is trivial but the thing that made it work for me was putting it in a separate script.

using UnityEngine;
using TMPro; //I used textmeshpro to display the text on my android

void Update()
{
       TextMeshPro ShowTouch = GetComponent<TextMeshPro>();
        if (ShowTouch != null)
        {
            ShowTouch.text = Input.GetTouch(0).phase.ToString();
        }
}

I don’t have a technical explanation as to why it worked for me but I’ll just say what I think happened.
Based off of many answers that I came across related to this issue, it’s got something do with how the the cycle of the script fails to catch the moment the state changes to Touchphase.Ended, thus leaving it at Touchphase.Move or whatever. Since my script was pretty long (my Update() was full of private methods), I tried creating a new script that only carried the touch detection logic.

…you can also store the phase using an if condition.

using UnityEngine;
using TMPro; //I used textmeshpro to display the text on my android

private bool _IsTouchZeroEnded = false;
void Update()
{
       TextMeshPro ShowTouch = GetComponent<TextMeshPro>();
       if (ShowTouch != null)
       {
              ShowTouch.text = Input.GetTouch(0).phase.ToString();
       }

       // if condition that sends the touch phase to a
       // public void (TouchphaseGetter(bool Phase)) that
       // was created in your main script (YourScript)
       if(Input.GetTouch(0).phase == Touchphase.Began)
       {
              IsTouchZeroEnded = true;
              YourScript somevariablename = GameObject.Find("GameObjectName").GetComponent<YourScript>();
              if(somevariablename != null)
              {
                     somevariablename.TouchphaseGetter(_IsTouchZeroEnded);[/INDENT]
              }
       }

}