Detect long touch in Unity 3D - c#

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;
}
}

Related

How to check if key has been clicked more than once or to find out how long a key has been pressed for c#

if(GetComponent<Rigidbody2D>().position.y >= -2.4){
abletojump = false;
}
else{
abletojump = true;
}
if(abletojump == true && Input.GetKey("space")){
transform.Translate(Vector3.up * 10 * Time.deltaTime);
}
I have made it so that if the object is over a specific height they shouldn't be allowed to jump. But it seems that when ever I set that height range it always seems to go lower than that so it repeatedly keeps jumping in the air. anyways knowing how to tell if a key has been pressed the an amount of times or to detect how long a key has been pressed for would really help.
Thanks.
Save the time when button is pushed Input.GetKeyDown and while the button is held downInput.GetKey find the difference between currentTime and the time that the button was pushed.
float timeThatshouldBePassed = 5f;
float pushStart = 0f;
void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
pushStart = Time.time;
}
if (Input.GetKey(KeyCode.Space)) {
if ((Time.time - pushStart) > timeThatshouldBebPassed) {
// doSomethings
}
}
}

Detecting difference in single tap and long tap

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
}
}
}

Timer in instantiation method skipped, logical error

I am somewhat new to C# and currently programming a method for instantiating objects in the Unity Editor.
The problem I am having with the code below is a logical error as follows: Instead of objects being created with 10 seconds in between, they are all spawned at the same time.
It is clear to me that this is a logical error. I find this hard to debug because it more so seems that my code for the timer simply isn't running. I've scratched my head as to why, can anyone help an aspiring rookie out? Thanks.
private void CreateObjects(GameObject objectToSpawn, float timer = 0.0f, float timerMax = 10.0f)
{
//If count of objects is less than the maximum constant, instantiate more at random web positions.
while (spiderCount < 7)
{
//StartCoroutine("SpiderGeneration");
timer += Time.deltaTime;
if (timer >= timerMax)
{
//spiderArray = GameObject.FindGameObjectsWithTag("spider");
//spiderCount = spiderArray.Length;
Debug.Log("spawnTime is set to true via ObjectGeneration");
//Generate a random number to choose from a series of set spawn points.
randPos = Random.Range(1, 4);
if (randPos == 1)
{
//Instantiate(object, position, quat);
Instantiate(objectToSpawn, rand1, transform.rotation);
Debug.Log("SPAWN 1");
timer = 0.0f;
}
else if (randPos == 2)
{
Instantiate(objectToSpawn, rand2, transform.rotation);
Debug.Log("SPAWN 2");
timer = 0.0f;
}
else if (randPos == 3)
{
Instantiate(objectToSpawn, rand3, transform.rotation);
Debug.Log("SPAWN 3");
timer = 0.0f;
}
else
{
Debug.Log("OBJECT GEN ERROR");
}
}
}
}
You could approach this in another way and code it simply using the Update() method that Unity calls every frame.
private int spiderCount;
private float timer;
public float TimerMax;
public List<Transform> spawnPoints = new List<Transform>(0);
private void Update ( )
{
timer += Time.deltaTime;
if ( spiderCount < 7 && timer >= TimerMax )
{
if ( spawnPoints.Count == 0 ) { Debug.LogError ( "You don't have any spawn points!" ); return; }
var spawnPointIndex = UnityEngine.Random.Range ( 0, spawnPoints.Count );
Instantiate ( objectToSpawn, spawnPoints[spawnPointIndex], transform.rotation );
++spiderCount;
timer = 0;
}
}
Not knowing how you'd like to set up your scene though, you might want ALL 7 spiders to appear straight away, with a 10 second (TimeMax) delay after that? But, either way, this should get you started.
I believe your issue is that this isn't being run in the update function. I'm fairly certain this would work if you were to call for the method to run in the update function and remove the while loop. This is because the Update() function runs once every frame, so when you add Time.deltaTime to a value, it will always only increase by 1 every second (allowing the timer you have here to work), however, what you have here does not add Time.deltaTime once every frame, it adds it recursively as long as your while(spiderCount < 7) returns true, this causes it to happen a bunch of times on the first frame, because it is reaching the timer value required on that first frame as is adding to the timer many times until it meets the criteria of the while function. This should definitely work if you do the following (also I read that you said you have the method in your FixedUpdate() method, you should only use FixedUpdate for physics updates, rather place it in your Update() method):
void Update()
{
CreateObjects({input the parameters here});
}
private void CreateObjects(GameObject objectToSpawn, float timer = 0.0f, float timerMax = 10.0f)
{
//If count of objects is less than the maximum constant, instantiate more at random web positions.
if (spiderCount < 7)
{
timer += Time.deltaTime;
if (timer >= timerMax)
{
randPos = Random.Range(1, 4);
if (randPos == 1)
{
//Instantiate(object, position, quat);
Instantiate(objectToSpawn, rand1, transform.rotation);
Debug.Log("SPAWN 1");
timer = 0.0f;
}
else if (randPos == 2)
{
Instantiate(objectToSpawn, rand2, transform.rotation);
Debug.Log("SPAWN 2");
timer = 0.0f;
}
else if (randPos == 3)
{
Instantiate(objectToSpawn, rand3, transform.rotation);
Debug.Log("SPAWN 3");
timer = 0.0f;
}
else
{
Debug.Log("OBJECT GEN ERROR");
}
}
}
}
I am fairly certain this should work, otherwise you can just use coroutines(which I would personally use), like so:
private void Start()
{
StartCoroutine(CreateObjects({input parameters here}));
}
IEnumerator CreateObjects(GameObject objectToSpawn, float timer = 0.0f, float timerMax = 10.0f)
{
//Wait for the allotted time amount (in this case 10 seconds)
yield return new WaitForSeconds(timerMax);
randPos = Random.Range(1, 4);
if (randPos == 1)
{
//Instantiate(object, position, quat);
Instantiate(objectToSpawn, rand1, transform.rotation);
Debug.Log("SPAWN 1");
timer = 0.0f;
}
else if (randPos == 2)
{
Instantiate(objectToSpawn, rand2, transform.rotation);
Debug.Log("SPAWN 2");
timer = 0.0f;
}
else if (randPos == 3)
{
Instantiate(objectToSpawn, rand3, transform.rotation);
Debug.Log("SPAWN 3");
timer = 0.0f;
}
else
{
Debug.Log("OBJECT GEN ERROR");
}
//If the spider count less than required, start the coroutine again to spawn another
if(spiderCount < 7)
{
StartCoroutine(CreateObjects({input parameters here}));
}
}
If you have any questions at all let me know, I am sure this isn't difficult at all to fix, just let me know if you need anything cleared up! (I also apologize if the beginning is a tad confusing to read, I confused myself at first and it shows through haha, but I cleared up when I got to the code)
Alright I have figured it out. To anyone reading this that had to same issues as me, my advice is as follows:
So you want a coroutine to start in the update function and not start multiple coroutines every frame, right? You need to set a flag for the coroutine. That was the answer. I simply set a one-time flag to activate upon starting the coroutine that prevents any more from starting until you decide to run it again.
Here is the code.
(Also I wanna say thanks to everyone that helped me out with this! I was able to find the right answer thanks to everyones input :) )
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class spiderGenerator : MonoBehaviour
{
public GameObject player;
public GameObject spiderPrefab;
public GameObject[] spiderArray = new GameObject[7];
public int spiderCount = 0;
private int randPos;
private Vector3 rand1;
private Vector3 rand2;
private Vector3 rand3;
bool inRange = false;
bool spawnerFlag = false;
// Start is called before the first frame update
void Start()
{
player = GameObject.FindGameObjectWithTag("Player");
rand1 = new Vector3(20.49f, 66.671f, 56.98f);
rand2 = new Vector3(8.88f, 66.671f, 61.65f);
rand3 = new Vector3(-1f, 66.671f, 65.52f);
}
// Update is called once per frame
void Update()
{
spiderArray = GameObject.FindGameObjectsWithTag("spider");
spiderCount = spiderArray.Length;
if (player.transform.position.z < 78 && player.transform.position.z > 47 && player.transform.position.x < 25 && player.transform.position.x > -6 && player.transform.position.y < 70 && player.transform.position.y > 60)
{
inRange = true;
}
else
{
inRange = false;
}
if (inRange == true && spawnerFlag == false)
{
StartCoroutine(CreateObjects(spiderPrefab));
Debug.Log("Coroutine CreateObjects for {0} STARTED", spiderPrefab);
spawnerFlag = true;
}
}
IEnumerator CreateObjects(GameObject objectToSpawn, float timerMax = 10.0f)
{
while (inRange == true && spiderCount < 7)
{
yield return new WaitForSeconds(timerMax);
Debug.Log("spawn for {0} is set to true via ObjectGeneration", objectToSpawn);
//Generate a random number to choose from a series of set spawn points.
randPos = Random.Range(1, 4);
if (randPos == 1)
{
//Instantiate(object, position, quat);
Instantiate(objectToSpawn, rand1, transform.rotation);
Debug.Log("SPAWN 1");
}
else if (randPos == 2)
{
Instantiate(objectToSpawn, rand2, transform.rotation);
Debug.Log("SPAWN 2");
}
else if (randPos == 3)
{
Instantiate(objectToSpawn, rand3, transform.rotation);
Debug.Log("SPAWN 3");
}
else
{
Debug.Log("OBJECT GEN ERROR");
}
}
Debug.Log("Coroutine TERMINATED");
yield break;
}
// need to put everything below the wait for 10 seconds to get results.
}

Changing a shader blend on pointer click over time

I have a shader in Unity that is set up to blend between two textures. This is set up and works fine.
Blend has a range from 0-1 and works well on offline mode.
_Blend1("Blend between _MainTex and Texture2", Range(0, 1)) = 0
I have set up an OnClick pointer that works fine. It essentially toggles a value to true and activates it in the Update. I have had some success on toggling the values between 0 (The first texture and 1 (The second texture).
public void OnColorChangeClick()
{
if (newSwitchOne)
{
switchOn = true;
}
else if (newSwitchTwo)
{
switchOff = true;
}
}
In the update when switchOn is true I have a while loop that runs and increments a count for the blend.
void Update()
{
rend.material.SetFloat("_Blend1", up);
while (switchOn == true) {
for (int i = 0; i < 10; i++)
{
//StartCoroutine(Wait());
up = Time.deltaTime * (up + 0.1f);
}
switchOn = false;
print(switchOn);
}
}
The trouble I am having is that the value increments are not working alongside time.deltaTime. I am seeing that Time.deltatime may not work in a while loop. So I have also tried a CoRoutine with a WaitForSeconds. None of this is giving me the incrementation that I desire.
I have looked into Lerping this - but I'm not aware how to Lerp such a value - I have been looking, perhaps in the wrong places.
Can someone point me in the right direction?
Time.deltaTime returns the time it took to complete the last frame so it will be roughly constant as you loop through (and a very small value) - which is keeping your up value to remain tiny. You should be able to lerp the value like so.
float t = 0;
void Update()
{
if (switchOn == true)
{
t += Time.deltaTime;
rend.material.SetFloat("_Blend1", Mathf.Lerp(0, 1f, t/3f)); //3 is time to complete lerp
}
if (t > 3) // lerp has completed. reset function and timer.
{
t = 0;
switchOn = false;
}
}
Update() already runs "over time." You don't need a for loop or a coroutine. You just need to track a variable that changes inside the Update() method body.
up = Time.deltaTime * (up + 0.1f);
Oh wait, you already do.
void Update()
{
rend.material.SetFloat("_Blend1", up);
if(switchOn == true) {
up = Time.deltaTime * (up + 0.1f);
if(up >= 1) {
switchOn = false;
}
print(switchOn);
}
}
Ta da.
However, you still need code to bring the value back down again (either animating it back to 0 or just setting it to 0) but your original code didn't have it either, so I'm not including it.

iterating through XNA gametime delay timer in arrays

I am trying iterate through an array of Bullets in order to set the position of each one with a delay of 0.3 secs.
FireTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (kbState.IsKeyDown(Keys.Space) && FireTimer > FireRate)
{
for (int i = 0; i < bullets.Count(); i++)
{
if (bullets[i].IsAlive == false)
{
bullets[i].IsAlive = true;
bullets[i].position = position;
FireTimer = 0f;
}
}
}
My FireTimer is assigned a value of 0.0f and the FireRate is assigned 0.3f.
All bullets are drawn and given the same position when I call this method. Can anybody tell me what needs to be done to get them to be positioned at the position in 0.3sec intervals?
You are checking the firetimer when the key is down, then you iterate over the entire array and fire each bullet. You will want a variable to store the current index in the clip, then increment it each time a bullet is fired.
int clipIndex = 0;
...
FireTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (kbState.IsKeyDown(Keys.Space) && clipIndex < bullets.Count() FireTimer > FireRate)
{
if (bullets[clipIndex].IsAlive == false)
{
bullets[i].IsAlive = true;
bullets[i].position = position;
FireTimer = 0f;
}
clipIndex++;
}
You could then reload by changing the index back to 0; Another thing to be aware of, if the bullet's flag IsAlive is true, then this miss fire for a single frame. If you wanted to just find the next alive bullet you could do a while statement:
while (bullets[clipIndex].IsAlive && clipIndex < bullets.Count())
clipIndex++;
if (clipIndex < bullets.Count())
...
The if statement changes because you know that clipIndex will be the index of a bullet that isn't alive, but you do want to make sure there IS a bullet.

Categories

Resources