I'd like to deplete value of health from set value every 1 second in Update method. I wrote the code, but it seems like it depletes faster than 1 second.
In an Update method, I call:
if (Hunger <= 0.0f)
{
userHealth -= HealthDepletionValue * Time.deltaTime;
}
Which should deplete set value of HealthDepletionValue every second Time.deltaTime.
When I run the game, it depletes HealthDepletionValue every 0.1 second or something along those lines. But surely not every 1 second, like I want to.
Did I miss something?
As stated in this post, Update() runs once per frame. So if you're game is running 30 frames/second, then Update() will execute 30 times/second.
To specify a function to be executed every x seconds, you can use InvokeRepeating. To do this, put the logic you want executed every second into it's own method:
void DepleteHealth()
{
if (Hunger <= 0.0f)
{
userHealth -= HealthDepletionValue * Time.deltaTime;
}
}
And then in the Start() method, call InvokeRepeating, passing in your method name and the number of seconds between execution of said method:
void Start()
{
InvokeRepeating("DepleteHealth", 1.0f, 1.0f);
}
The post above also shows alternative ways to handle this. Including tracking a second counter within the Update() method, to ensure that you're logic is only executed if the counter has passed a full second.
Another option to InvokeRepeating would be to create your own timer.
float timer = 0;
Update()
{
if (Hunger <= 0.0f)
{
timer += time.deltaTime;
if (timer > 1f){
userHealth -= HealthDepletionValue;
timer = 0; //reset timer
}
}
}
Alternatively you could use a Coroutine and the WaitForSeconds class.
public void Start () {
StartCoroutine(DepleteHealth(TimeSpan.FromSeconds(1), 5));
}
public IEnumerator DepleteHealth (TimeSpan frequency, int loss) {
var wait = new WaitForSeconds((float)frequency.TotalSeconds());
while(true) {
userHealth -= loss;
yield return wait;
}
}
This can be stopped and started pretty easily.
Related
Here is my code.. I have explained the issue after..
public bool isPipeActive;
[SerializeField] private float timer;
void Start()
{
isPipeActive = false;
timer = 0f;
}
void FixedUpdate()
{
if (Input.GetKeyDown(KeyCode.Backspace))
{
isPipeActive = !isPipeActive; //bool flag invert
}
if (isPipeActive == true) //start pipe animations
{
for (int j = 0; j < ParentOf_arrow.transform.childCount; j++)
{
Initiate_arrow_FLOW_INDICATOR(j);
StartCoroutine(Time_Delay());
//while (timer < 1.0f)
for (timer = 0f; timer < 2.0f; timer += Time.fixedDeltaTime)
{
//just a blank function to see if it creates a delay..
timer += Time.fixedDeltaTime;
}
//timer = 0f;
}
}
}
IEnumerator Time_Delay()
{
yield return new WaitForSeconds(2f);
Debug.Log("2s delay initiated");
}
Initiate_arrow_FLOW_INDICATOR(j) visibly changes the color of one 3D arrow in a sequence of 3D arrows (arrow1, then arrow2, etc) to show direction. something like this.. i just want to add a time delay in between each color change, but none of the attempts i've made seem to create a time delay when i enter play mode and press Backspace key.
the colors flash extremely fast. There is no waiting between color changes whatsoever.. even when running the coroutine, while loop and for loop together :(
What is going on and how do i fix it??
The issue with the for loop is that you aren't waiting for the actual time between fixed updates and rather using the time between the last fixed update and adding it as fast as your CPU will until timer is larger than 2.
The coroutine will wait 2 seconds before logging it's output.
You need to put your arrow change code in the coroutine, because the Coroutine essentially works in "parallel" to your FixedUpdate.
So either do:
void FixedUpdate()
{
timer += Time.deltaTime;
if(timer >= 2.0f) {
//arrow thingy
timer = 0;
}
}
or in the Coroutine:
IEnumerator TimeDelay()
{
yield return new WaitForSeconds(2f);
//arrow thingy
}
In this state the FixedUpdate version will loop indefinitely and call arrow thingy roughly each two seconds, and the Coroutine will do so once.
A quick side note you should probably use Update and DeltaTime for this and not FixedUpdate with FixedDeltaTime.
I am learning Unity from a Swift SpriteKit background where moving a sprite's x Position is as straight forward as an running an action as below:
let moveLeft = SKAction.moveToX(self.frame.width/5, duration: 1.0)
let delayAction = SKAction.waitForDuration(1.0)
let handSequence = SKAction.sequence([delayAction, moveLeft])
sprite.runAction(handSequence)
I would like to know an equivalent or similar way of moving a sprite to a specific position for a specific duration (say, a second) with a delay that doesn't have to be called in the update function.
gjttt1's answer is close but is missing important functions and the use of WaitForSeconds() for moving GameObject is unacceptable. You should use combination of Lerp, Coroutine and Time.deltaTime. You must understand these stuff to be able to do animation from Script in Unity.
public GameObject objectectA;
public GameObject objectectB;
void Start()
{
StartCoroutine(moveToX(objectectA.transform, objectectB.transform.position, 1.0f));
}
bool isMoving = false;
IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;
float counter = 0;
//Get the current position of the object to be moved
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
isMoving = false;
}
Similar Question: SKAction.scaleXTo
The answer of git1 is good but there is another solution if you do not want to use couritines.
You can use InvokeRepeating to repeatedly trigger a function.
float duration; //duration of movement
float durationTime; //this will be the value used to check if Time.time passed the current duration set
void Start()
{
StartMovement();
}
void StartMovement()
{
InvokeRepeating("MovementFunction", Time.deltaTime, Time.deltaTime); //Time.deltaTime is the time passed between two frames
durationTime = Time.time + duration; //This is how long the invoke will repeat
}
void MovementFunction()
{
if(durationTime > Time.time)
{
//Movement
}
else
{
CancelInvoke("MovementFunction"); //Stop the invoking of this function
return;
}
}
You can use co-routines to do this. To do this, create a function that returns type IEnumerator and include a loop to do what you want:
private IEnumerator foo()
{
while(yourCondition) //for example check if two seconds has passed
{
//move the player on a per frame basis.
yeild return null;
}
}
Then you can call it by using StartCoroutine(foo())
This calls the function every frame but it picks up where it left off last time. So in this example it stops at yield return null on one frame and then starts again on the next: thus it repeats the code in the while loop every frame.
If you want to pause for a certain amount of time then you can use yield return WaitForSeconds(3) to wait for 3 seconds. You can also yield return other co-routines! This means the current routine will pause and run a second coroutine and then pick up again once the second co-routine has finished.
I recommend checking the docs as they do a far superior job of explaining this than I could here
I am learning Unity from a Swift SpriteKit background where moving a sprite's x Position is as straight forward as an running an action as below:
let moveLeft = SKAction.moveToX(self.frame.width/5, duration: 1.0)
let delayAction = SKAction.waitForDuration(1.0)
let handSequence = SKAction.sequence([delayAction, moveLeft])
sprite.runAction(handSequence)
I would like to know an equivalent or similar way of moving a sprite to a specific position for a specific duration (say, a second) with a delay that doesn't have to be called in the update function.
gjttt1's answer is close but is missing important functions and the use of WaitForSeconds() for moving GameObject is unacceptable. You should use combination of Lerp, Coroutine and Time.deltaTime. You must understand these stuff to be able to do animation from Script in Unity.
public GameObject objectectA;
public GameObject objectectB;
void Start()
{
StartCoroutine(moveToX(objectectA.transform, objectectB.transform.position, 1.0f));
}
bool isMoving = false;
IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;
float counter = 0;
//Get the current position of the object to be moved
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
isMoving = false;
}
Similar Question: SKAction.scaleXTo
The answer of git1 is good but there is another solution if you do not want to use couritines.
You can use InvokeRepeating to repeatedly trigger a function.
float duration; //duration of movement
float durationTime; //this will be the value used to check if Time.time passed the current duration set
void Start()
{
StartMovement();
}
void StartMovement()
{
InvokeRepeating("MovementFunction", Time.deltaTime, Time.deltaTime); //Time.deltaTime is the time passed between two frames
durationTime = Time.time + duration; //This is how long the invoke will repeat
}
void MovementFunction()
{
if(durationTime > Time.time)
{
//Movement
}
else
{
CancelInvoke("MovementFunction"); //Stop the invoking of this function
return;
}
}
You can use co-routines to do this. To do this, create a function that returns type IEnumerator and include a loop to do what you want:
private IEnumerator foo()
{
while(yourCondition) //for example check if two seconds has passed
{
//move the player on a per frame basis.
yeild return null;
}
}
Then you can call it by using StartCoroutine(foo())
This calls the function every frame but it picks up where it left off last time. So in this example it stops at yield return null on one frame and then starts again on the next: thus it repeats the code in the while loop every frame.
If you want to pause for a certain amount of time then you can use yield return WaitForSeconds(3) to wait for 3 seconds. You can also yield return other co-routines! This means the current routine will pause and run a second coroutine and then pick up again once the second co-routine has finished.
I recommend checking the docs as they do a far superior job of explaining this than I could here
I am learning Unity from a Swift SpriteKit background where moving a sprite's x Position is as straight forward as an running an action as below:
let moveLeft = SKAction.moveToX(self.frame.width/5, duration: 1.0)
let delayAction = SKAction.waitForDuration(1.0)
let handSequence = SKAction.sequence([delayAction, moveLeft])
sprite.runAction(handSequence)
I would like to know an equivalent or similar way of moving a sprite to a specific position for a specific duration (say, a second) with a delay that doesn't have to be called in the update function.
gjttt1's answer is close but is missing important functions and the use of WaitForSeconds() for moving GameObject is unacceptable. You should use combination of Lerp, Coroutine and Time.deltaTime. You must understand these stuff to be able to do animation from Script in Unity.
public GameObject objectectA;
public GameObject objectectB;
void Start()
{
StartCoroutine(moveToX(objectectA.transform, objectectB.transform.position, 1.0f));
}
bool isMoving = false;
IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;
float counter = 0;
//Get the current position of the object to be moved
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
isMoving = false;
}
Similar Question: SKAction.scaleXTo
The answer of git1 is good but there is another solution if you do not want to use couritines.
You can use InvokeRepeating to repeatedly trigger a function.
float duration; //duration of movement
float durationTime; //this will be the value used to check if Time.time passed the current duration set
void Start()
{
StartMovement();
}
void StartMovement()
{
InvokeRepeating("MovementFunction", Time.deltaTime, Time.deltaTime); //Time.deltaTime is the time passed between two frames
durationTime = Time.time + duration; //This is how long the invoke will repeat
}
void MovementFunction()
{
if(durationTime > Time.time)
{
//Movement
}
else
{
CancelInvoke("MovementFunction"); //Stop the invoking of this function
return;
}
}
You can use co-routines to do this. To do this, create a function that returns type IEnumerator and include a loop to do what you want:
private IEnumerator foo()
{
while(yourCondition) //for example check if two seconds has passed
{
//move the player on a per frame basis.
yeild return null;
}
}
Then you can call it by using StartCoroutine(foo())
This calls the function every frame but it picks up where it left off last time. So in this example it stops at yield return null on one frame and then starts again on the next: thus it repeats the code in the while loop every frame.
If you want to pause for a certain amount of time then you can use yield return WaitForSeconds(3) to wait for 3 seconds. You can also yield return other co-routines! This means the current routine will pause and run a second coroutine and then pick up again once the second co-routine has finished.
I recommend checking the docs as they do a far superior job of explaining this than I could here
Ok pretty much what I am trying to do is have my program wait a predetermined amount of times then move the character to another spot on the grid (which is notated by the "panel_x" and "panel_y" variables). Instead it waits and then moves the character around every frame...Im not sure what I am doing wrong. I believe I need a coroutine, but I may be wrong.
//How I am calling the coroutine
void Update()
{
if(HP != 0)
{
StartCoroutine(Wait());
}
}
//The Coroutine I need to run to move my character
//around...I need this to run until the character's
//hp reaches 0.
IEnumerator Wait()
{
while(true)
{
//I need it to wait...
yield return new WaitForSeconds(3);
//Then move the character to another
//grid...
panel_x = Random.Range(1, 4);
panel_y = Random.Range(1, 4);
}
}
Update runs on every frame. What's happening here is that you're calling Wait() on every frame which will run multiple instances in an infinite loop.
I believe what you're trying to do is change the x and y values every 3 seconds.
If so, try something like this.
float timer = 0.0f;
void Update()
{
if (HP != 0)
{
timer += Time.deltaTime;
if (timer >= 3)
{
panel_x = Random.Range(1, 4);
panel_y = Random.Range(1, 4);
timer = 0;
}
}
}
Time.deltaTime returns the time passed between frames so it can be used as a timer by summing it every frame. When 3 seconds has passed, we reset the timer to 0 and run our method.
Did you modify that HP ever after? If HP is not 0, then the StartCoroutine(Wait()) will be called every time Update() is executed. That is why you get the weird result.
Your function doesn't seem to take any parameters, so I can provide you other method like Invoke as an alternative.
Invoke(x, y) executes a function after a certain amount of seconds delay. In your case for example:
void Update(){
if(HP != 0)
Invoke("MoveChar", 3); //execute MoveChar() after 3 seconds
}
MoveChar(){
panel_x = Random.Range(1,4);
panel_y = Random.Range(1,4);
}
Just for your info, there is also InvokeRepeating(x, y, z) which maybe you won't need in this case.
It repeats every z seconds after function x being called with y delay.
void Start(){
//execute MoveChar() after 3 seconds and call the
//function again after 0.5 seconds repeatedly
InvokeRepeating("MoveChar", 3, 0.5);
}
MoveChar(){
panel_x = Random.Range(1,4);
panel_y = Random.Range(1,4);
}