I have implemented single tap and long tap functions. The long tap gets activated if the tap is held for 0.5 seconds and function gets executed only if the touch is released. How do I avoid this?
Such that the long tap function should get activated when the touch is pressed and held for 0.5 seconds instead of releasing the touch?
private float TouchTime = 0;
void Update () {
Touch touch = Input.touches[0];
if (touch.phase == TouchPhase.Began) {
TouchTime = Time.time;
}
if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled) {
if (Time.time - TouchTime <= 0.5) {
//Single Tap
} else {
//Long Tap
}
}
Instead of waiting for the touch to end you could do e.g.
pubic float longPressTime = 0.5f;
bool alreadyFiredLongPress;
float timeElapsed;
void Update ()
{
// First of all this is how you get the touches!
// Check if any touch even exists
// and then do not go through Input.touches which is very expensive!
if(Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
switch(touch.phase)
{
case TouchPhase.Began:
// Start/Reset the timer
timeElapsed = 0;
alreadyFiredLongPress = false;
break;
case TouchPhase.Stationary:
// Only fire the long press once
if(!alreadyFiredLongPress)
{
// increase by the time passed since last frame
timeElapsed += Time.deltaTime;
// pressed long enough?
if(timeElapsed >= longPressTime)
{
// block the single tap and other long press events
alreadyFiredLongPress = true;
// Long Tap
}
}
break;
// in all other cases
case TouchPhase.Moved:
case TouchPhase.Ended:
case TouchPhase.Canceled:
// Fire single tap if no long tap was trigdered before
if(!alreadyFiredLongPress)
{
//Single Tap
}
break;
}
}
}
This is hopefully simpler
private float TouchTime = 0;
private float _longTouchThreshold = 0.5f; // minimum time in seconds for long touch
private bool _isTouching = false;
void Update ()
{
Touch touch = Input.touches[0];
if (touch.phase == TouchPhase.Began) {
TouchTime = Time.time;
_isTouching = true;
}
// touch has started
if (_isTouching)
{
// minimum time has passed for long touch
if (Time.time - TouchTime >= _longTouchThreshold) {
_isTouching = false;
// long touch
}
// minimum time hasn't passed but touch ended
else if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled) {
_isTouching = false;
// single tap
}
}
}
Related
I'm trying to detect long touch on the screen by using Time.time or Time.deltatime, but nothing works.
It should turn out like this: after beginning TouchPhase.Stationary (or Began) timer should goes on, then after TouchPhase.Ended timer can be reset to 0.
But here is condition that checks, if I held the button on the sensor less than 0.5 sec - do something.
Problems:
Time.Deltatime for some reason it does not add up to a variable for each iteration, but constantly assigns a new value equal to the time of the last frame, somewhere around 0.02-0.1
Time.time constantly adds a value to the variable, when subtracting the time of pressing the screen and the vacation time, the variable that should be reset to zero, but with each iteration of actions, the value for some reason cannot vary from 0.0 - 0.05, the timer for some reason constantly sums up the value and the variable constantly increases
My ask: how can I track how many seconds my finger was on the screen?
void TapOrLongTounch()
{
float pressTime = 0;
float endPressTime = 0;
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Stationary)
{
pressTime += Time.time;
}
if (touch.phase == TouchPhase.Ended)
{
endPressTime = Time.time - pressTime;
if (endPressTime < 0.5f)
{
//Do something;
}
endPressTime = 0;
}
}
}
First of all Time.time is
This is the time in seconds since the start of the application [...].
What you are doing is basically exponentially adding more and more time every frame.
→ What you rather want to count is the Time.deltaTime
The interval in seconds from the last frame to the current one [...].
like
if (touch.phase == TouchPhase.Stationary)
{
pressTime += Time.deltaTime;
}
so that pressTime results in the total amount of time in seconds the touch was stationary.
BUT you also have it as a method variable so it continuously starts from 0 anyway!
→ You rather want to have a class field for it!
Then further the
if (touch.phase == TouchPhase.Ended)
{
endPressTime = Time.time - pressTime;
...
makes no sense / is pretty redundant. The pressTime already is the total time in seconds the touch was stationary!
In general I would rather use a switch-case and do e.g.
float pressTime = 0;
void TapOrLongTounch()
{
if (Input.touchCount <= 0) return;
var touch = Input.GetTouch(0);
switch(touch.phase)
{
// Maybe you also want to reset when the touch was moved?
//case TouchPhase.Moved:
case TouchPhase.Began:
pressTime = 0;
break;
case TouchPhase.Stationary:
pressTime += Time.deltaTime;
break;
case TouchPhase.Ended:
case TouchPhase.Canceled:
if (pressTime < 0.5f)
{
//Do something;
}
pressTime = 0;
break;
}
}
I am trying to make enemy patrolling system, where evrytime guard reaches his point, he stopes for 10 seconds, and then continue his movement. I've tried combining animations from Blend tree with isStopped property from NavMeshAgent.
EDIT: My current script makes agent move to point, then he stopes for some time, and then only walk animation plays, but he staing on one place.
public Transform[] points;
private int destPoint = 0;
public NavMeshAgent agent;
public Animator animator;
public int time;
void Start()
{
agent = GetComponent<NavMeshAgent>();
animator = transform.Find("Enemy").GetComponent<Animator>();
// Disabling auto-braking allows for continuous movement
// between points (ie, the agent doesn't slow down as it
// approaches a destination point).
//agent.autoBraking = false;
}
void GotoNextPoint()
{
// Returns if no points have been set up
if (points.Length == 0)
return;
// Set the agent to go to the currently selected destination.
agent.destination = points[destPoint].position;
// Choose the next point in the array as the destination,
// cycling to the start if necessary.
destPoint = (destPoint + 1) % points.Length;
//agent.speed = 1f;
//animator.SetFloat("Blend", agent.speed);
}
void Update()
{
if (agent.remainingDistance == 0f && time == 100000)
{
agent.speed = 1f;
Debug.Log(agent.remainingDistance);
animator.SetFloat("Blend", 1);
GotoNextPoint();
}
else if (agent.remainingDistance <= 0.5f && agent.remainingDistance != 0f && time == 100000)
{
animator.SetFloat("Blend",0);
agent.enabled = false;
GotoNextPoint();
}
else if(animator.GetFloat("Blend") == 0)
{
time--;
}
if (time == 99000 && animator.GetFloat("Blend") == 0)
{
time = 10000;
agent.enabled = true;
agent.isStopped = false;
animator.SetFloat("Blend", 1);
agent.autoRepath = true;
GotoNextPoint();
}
}
I changed few lines of code, now agent moves after first stop, but second time he stops at second poitm,walking animation still working, time doesn't decrementing
if (time == 99000 && animator.GetFloat("Blend") == 0)
{
time = 10000;
agent.enabled = true;
agent.isStopped = false;
animator.SetFloat("Blend", 1);
//New lines of code
agent.ResetPath();
destPoint = (destPoint + 1) % points.Length;
agent.SetDestination(points[destPoint].position);
}[enter image description here][1]
First of all, I would use the "SetDestination" function in order to set the next destination.
In the end you wrote:
if (time == 99000 && animator.GetFloat("Blend") == 0)
{
time = 10000; *-> needs to be 100000, not 10000*
agent.enabled = true;
agent.isStopped = false;
animator.SetFloat("Blend", 1);
agent.autoRepath = true;
GotoNextPoint();
}
You can use "NavMeshAgent.ResetPath" to reset the path instead of using "autoRepath".
After the "ResetPath", use the "SetDestination" inside the function GotoNextPoint().
One more very important thing - Check that there is more than one point.
If there is just one point, your guard will just walk at the same spot.
For more information, is suggest to check out Unity NavMeshAgent
I am having difficulty delaying the movement of my character in Unity 2018.4, I suspect this is more of just a problem with my code as apposed to unity.
Edit:
I want to be able to hold down the right arrow key, have the character not move for 415ms, then be able to move for 580ms, after which he cannot move for 350ms (to let the animation finish playing)
I tried using countdown in a IE numerator to wait and then call my movement function but this results in my character not moving after the animation has played.
//What makes the character move, works fine if executed on its own in update
private void ExecuteMovement(float dir)
{
currentPosition = transform.position;
currentPosition.x += dir * Time.deltaTime * speed;
transform.position = currentPosition;
}
//Trying to use waitforseconds to delay the execution while the animation plays
private IEnumerator Movement(float dir)
{
yield return new WaitForSeconds(0.5f);
ExecuteMovement(dir);
}
void Update()
{
if (0 > Input.GetAxis("Horizontal"))
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 180, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
Movement(Input.GetAxis("Horizontal"));
}
else if (0 < Input.GetAxis("Horizontal"))
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 0, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
Movement(Input.GetAxis("Horizontal"));
}
}
I'm happy to try any alternatives to delaying movement of the character. The animation shows the character charging up to jump and then jumps. I want to only move while it is in the air which is approximately half a second into the animation.
You are trying to call your IEnumerator like a method.
Instead you have to start it as a Coroutine using StartCoroutine
StartCoroutine(Movement(Input.GetAxis("Horizontal")));
Saw your edit
I want to be able to hold down the right arrow key, have the character not move for 415ms, then be able to move for 580ms, after which he cannot move for 350ms (to let the animation finish playing)
until now so since you want a continues movement but still fully control the speed I would keep it in Update and use the coroutine only for controlling a flag. Coroutine is still way better to read/write and maintain. (Now it also allows direction switch midair .. not sure if you want this or not):
// control the ability to move with a simple flag
// instead of various states
public bool canMove;
// check if a jumping is already being animated/executed
// -> disallow a new jumping animation until it is finished
private bool canJump = true;
private void Update()
{
// avoid repeated API calls
var input = Input.GetAxis("Horizontal");
if (input < 0)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 180, 0);
if(canJump)
{
//starts the movement
StartCoroutine(Jump());
}
}
else if (input > 0)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 0, 0);
if(canJump)
{
//starts the movement
StartCoroutine(Jump());
}
}
// if can not move do nothing else
if(!canMove) return;
// execute the movement
transform.position += Vector3.right * input * speed * Time.deltaTime;
}
private IEnumerator Jump()
{
// disable jumping
canJump = false;
// disable move during animation
canMove = false;
//starts animation
animator.SetBool("isSkipping", true);
// not move for 415ms
yield return new WaitForSeconds(0.415f);
// enable move
canMove = true;
// be able to move for 580ms
yield return new WaitForSeconds(0.580f);
// disable move
canMove = false;
// cannot move for 350ms during end of animation
yield return new WaitForSeconds(0.350f);
// enable move again
canMove = true;
// re-enable jumping
canJump = true;
}
How about using timer in Update?
float timer = 0f;
void Update()
{
var dir = Input.GetAxis("Horizontal");
if (0 > dir)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 180, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
timer +=Time.deltaTime;
if(timer >= 0.5){
ExecuteMovement(dir);
timer -=0.5f;
}
//Movement(Input.GetAxis("Horizontal"));
}
else if (0 < dir)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 0, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
timer +=Time.deltaTime;
if(timer >= 0.5){
ExecuteMovement(dir);
timer -=0.5f;
}
//Movement(Input.GetAxis("Horizontal"));
}
}
I found a rather crude way of making this work after heavily modifying Gray_Rhino's answer to this question. I set up 3 stages of my jump animation, when continually moving one direction a timer will check to see how far into the animation it is and decide whether or not to allow movement input. the timer resets after the animation is finished or the character is told to move in the other direction.
bool facingRight;
int jumpPhase = 1;
float timer = 0f;
float dir;
void Update()
{
if (jumpPhase != 3)
{
dir = Input.GetAxis("Horizontal");
}
if (0 > dir || (jumpPhase == 3 && facingRight == true))
{
timer += Time.deltaTime;
if (facingRight != true)
{
transform.eulerAngles = new Vector3(0, 180, 0);
jumpPhase = 1;
timer = 0f;
facingRight = true;
}
else if (jumpPhase == 1)
{
//starts animation
animator.SetBool("isSkipping", true);
if (timer >= 0.415f)
{
jumpPhase = 2;
}
}
else if (jumpPhase == 2)
{
ExecuteMovement(dir);
if (timer >= 0.995f)
{
jumpPhase = 3;
}
}
if (jumpPhase == 3)
{
if (timer >= 1.5f)
{
jumpPhase = 1;
timer = 0f;
}
}
}
else if (0 < dir || (jumpPhase == 3 && facingRight != true))
{
timer += Time.deltaTime;
if (facingRight == true)
{
transform.eulerAngles = new Vector3(0, 0, 0);
jumpPhase = 1;
timer = 0f;
facingRight = false;
}
else if (jumpPhase == 1)
{
//starts animation
animator.SetBool("isSkipping", true);
if (timer >= 0.415f)
{
jumpPhase = 2;
}
}
else if (jumpPhase == 2)
{
ExecuteMovement(dir);
if (timer >= 0.995f)
{
jumpPhase = 3;
}
}
if (jumpPhase == 3)
{
if (timer >= 1.5f)
{
jumpPhase = 1;
timer = 0f;
}
}
}
}
i want to implement the long tap on the screen..If the user long taps on the screen the position of the x and y axis decreases and as soon as he releases the tap the x increases and the y decreases. i have messed around with somethings but no luck..heres the code i have been trying.
public class move : MonoBehaviour
{
public Vector2 velocity = new Vector2(40,40);
public float forwardspeed=0.02f;
Vector2 movement;
// Use this for initialization
void Start ()
{
Debug.Log("start+"+Input.touchCount);
movement.x+=(forwardspeed);
movement.y-=(forwardspeed);
rigidbody2D.velocity = movement;
}
// Update is called once per frame
void FixedUpdate ()
{
int i=0;
while (i < Input.touchCount)
{
// Is this the beginning phase of the touch?
if (Input.GetTouch(i).phase == TouchPhase.Began)
{
Debug.Log("input touch count"+Input.touchCount);
// rigidbody2D.gravityScale =0;
movement.x+=(forwardspeed);
movement.y+=(forwardspeed);
rigidbody2D.velocity = movement;
}
else if (Input.GetTouch(i).phase == TouchPhase.Ended)
{
movement.x += (forwardspeed);
movement.y -= (forwardspeed);
rigidbody2D.velocity = movement;
}
++i;
}
}
}
You can use Input.touchCount to do exactly what you need with very simple code.
If you want some behavior to occur while the user is touching the screen, that means you want the behavior to occur while Input.touchCount is non-zero. For example,
void FixedUpdate() {
if(Input.touchCount > 0) { //user is touching the screen with one or more fingers
//do something
} else { //user is not currently touching the screen
//do something else
}
}
Specific to your code, you would want to set the character's velocity to some value while Input.touchCount is zero, and then set it to a different value while it's not zero.
void FixedUpdate() {
if(Input.touchCount > 0) {
rigidbody2D.velocity = new Vector2(forwardspeed, forwardspeed);
} else {
rigidbody2D.velocity = new Vector2(forwardspeed, -forwardspeed);
}
}
Note the negative sign in the else block. Instead of adding and subtracting values like you were doing before, we're just setting the velocity to +/- depending on the state.
So here's what I'd like to do: The player is idling on the ground, not moving at all. And after some time a random idle animation should be played. How do I detect that the player hasn't been moving for a certain amount of time?
IEnumerator Idle()
{
// check if player is idling on the ground
if (grounded && (_controller.velocity.x == 0))
{
// Now what?
//...
}
idleIndex = IdleRandom();
_animator.SetInteger("IdleIndex", idleIndex);
_animator.SetTrigger("Idle");
}
int IdleRandom()
{
// choose random index of idle animations
int i = Random.Range(0, numberOfIdleAnims);
// if it's the same as the previous one...
if (i == idleIndex) {
// try another one
return IdleRandom ();
}
else
return i;
}
I've already set up my animator controller so that it would play one of the idle animations (chosen by the idleIndex) if the idle-Trigger is pushed. The only thing I cannot figure out is the not-moving-in-certain-time thing!
You need to count the time your player has been idle, not moving. You can do this in the void FixedUpdate() function.
I have drastically changed my code. Make sure you include the code for playing animations.
public class IdleManager : MonoBehaviour
{
private isIdle = false;
private float previousTime;
private const float IDLE_TIME = 5.0f;
void Update()
{
if(!isIdle && grounded && (_controller.velocity.x == 0))
{
isIdle = true;
previousTime = Time.timeSinceLevelLoad();
}
else if(isIdle && Time.timeSinceLevelLoad - previousTime > IDLE_TIME)
{
// Play animation here.
// Reset previousTime.
previousTime = Time.timeSinceLevelLoad;
}
else if(grounded || _controller.velocity.x > 0)
isIdle = false;
}
}