Time.DeltaTime suddenly stops working in Unity - c#

So I've been making a 2D shooter game in unity and I run my game on a global timer using the code
timer += Time.deltaTime
this usually just gives me a timer that works like a stop watch however suddenly the value of my timer variable just fluctuates at around 0.0003-0.0001 when running the script when it should be going 1 2 3 4 5 etc for every second. It has done it before and without me changing anything to my knowledge it just fixed itself after 30ish mins but now it's happened again. Does anyone know why this is?

float time = 0f;
float timer = 5f;
void Update()
{
time += Time.deltaTime;
if(time >= timer)
{
// Do something
}
}
The code is adding 1f to time every second. Using Time.deltaTime makes it so that it will be the same at any framerate (for example if the framerate is 30fps, it won't add a second 30 times in one second ) and if time (the amount of seconds) surpasses timer, we run what is in between the if statement.

Related

My coroutine is running slower on Unity android aplication

I'm working on a cookie clicker replica.
What happens is that I am using a yield return new WaitForSeconds (0.1f) to add the cookies per second to the cookie counter, and I want them to add up in a fast way, and not second per second.
The problem is that when I run it on my computer, it works perfectly, but when I run it on my android, it slowly adds the cookies to the counter.
According to my little knowledge, I would say it has to do with the FPS. I tried to use the (Time.deltaTime * 0.1f), inside the WaitForSeconds, but it still works slowly in android. Also tried using the Invoke() method, but it works slowly too.
(I have the StartCoroutine inside the Update method.)
(When I put 1 second instead of 0.1f, it works fine, but it doesn't look good in terms of appearance.)
IEnumerator Rps()
{
Stats.CookiesTotal = Stats.CookiesTotal + Stats.cookiesPerSecond;
yield return new WaitForSeconds(Time.deltaTime * 0.1f);
yield return null;
}
private void Update()
{
StartCoroutine(Rps());
openSlot(price1text, price1, slot1);
openSlot(price2text, price2, slot2);
openSlot(price3text, price3, slot3);
}
You are calling the coroutine from Update() which is framerate dependent.
So on a 60 fps machine, it will run Rps() 60 times, while on a 30 fps machine, it will run Rps() 30 times. Thus a faster fps machine will generate cookies faster.
Also note that the WaitForSeconds is occurring after you have increased the cookie amount which means it is delaying nothing at all, so in effect, this code is the same as:
private void Update()
{
Stats.CookiesTotal = Stats.CookiesTotal + Stats.cookiesPerSecond;
//...
}
As others have suggested, using coroutines this way in Update() is not ideal. It is possible to achieve a coroutined constant cookie increase every 0.1s by starting a coroutine in Start() and having an infinite loop inside the coroutine which increases cookie count and waits for X time. You will need to be very mindful of managing that infinite loop coroutine.
(This code is not tested but should give you the general idea)
private IEnumerator countCookiesCoroutine;
IEnumerator Rps()
{
while (true){
Stats.CookiesTotal = Stats.CookiesTotal + Stats.cookiesPerSecond;
yield return new WaitForSeconds(0.1f);
}
}
private void Start()
{
countCookiesCoroutine = Rps();
StartCoroutine(countCookiesCoroutine);
}
A more straightforward approach, especially if you are working with large numbers of cookies and precision is not important, might be to increment the cookie count in FixedUpdate() based on the physics time step, such as:
private void FixedUpdate(){
Stats.CookiesTotal = Stats.CookiesTotal + (Stats.cookiesPerSecond * Time.fixedDeltaTime);
}
This will make the cookie rate increase as smoothly as physics updates and framerate independent. The reason I have used FixedTime() rather than Update() is because Update() is framerate dependant and even with Time.deltaTime faster machines will still collect cookies faster due to the higher compound cookie interest being calculated more frequently than on slower fps machines.
You may also need to wrap the display of the cookie numbers in a Mathf.Floor() if displaying decimals is undesirable.

What main function does Time.deltaTime perform in Unity?

I'm learning game development in Unity. I recently got struct to the Time.deltaTime function during the code i was learning from the tutorials. I have searched about it for better understanding but not learned the main purpose of using it as it is explained in a professional way. In short I want some easy explanation. So, I can understand from it.
Rendering and script execution takes time. It differs every frame. If you want ~60 fps, you will not have stable fps - but differing amounts of time passing each frame. You could wait if you are too fast, but you cannot skip rendering when you are slower than expected.
To handle different lenghts of frames, you get the "Time.deltaTime". In Update it will tell you how many seconds have passed (usually a fraction like 0.00132) to finish the last frame.
If you now move an object from A to B by using this code:
object.transform.position += Vector3.forward * 0.05f;
It will move 0.05 Units per frame. After 100 frames it has moved 5 Units.
Some pcs may run this game at 60fps, others at 144 hz. Plus the framerate is not constant.
So your object will take 1-5 seconds to move this distance.
But if you do this:
object.transform.position += Vector3.forward * 5f * Time.deltaTime;
it will move 5 units in 1 second. Independent of the framerate. Because if you have 10000 fps the deltaTime will be very very small so it only moves a very tiny bit each frame.
Note: In FixedUpdate() its technically not 100% the same every frame, but you should act like it's always 0.02f or whatever you set the physics interval to. So independent from the framerate, the Time.deltaTime in FixedUpdate() will return the fixedDeltaTime
edit:
Here is a nice comparison video I made:
please watch the video on imgur
Red Cube: transform.position += Vector3.right * (1f / 60f);
Green Cube: transform.position += Vector3.right * Time.deltaTime;
framerate is locked to 60 fps! (using Application.targetFrameRate = 60;
You can see, the green cube is hitting the right wall always at 5, 10, 15, 20, 25 ...
At first they look synchronized, but once I jiggle the window, some frames are dropped (may be an editor thing, but there are tons of reasons for frame-drops in a real game). The red cube never catches up to the green one, the dropped frames are never repeated.
That shows using deltaTime is a lot more consistent + it gives you a nice bonus: You can adjust the timescale using Time.timeScale = .5f; to make a slowmotion!
Time.deltaTime is simply the time in seconds between the last frame and the current frame.
Since Update is called once per frame, Time.deltaTime can be used to make something happen at a constant rate regardless of the (possibly wildly fluctuating) framerate.
So if we just say obj.transform.position += new Vector3(offset, 0, 0); in our update function then obj will move by offset units in the x direction every frame, regardless of FPS.
However if we instead say obj.transform.position += new Vector3(offset * Time.deltaTime, 0, 0); then we know that obj will move offset units every second as every frame it will move the fraction of offset corresponding to how much time that frame took.
As said here:
This property provides the time between the current and previous frame.
You can use this to check your framerate, for example. Also, as said here
If you want to move something at a constant velocity speed, for instance, multiply speed by Time.deltaTime and you will get exactly the distance moved by the object since last Update (remember: distance = velocity * time)

Time doesn't correctly update in Unity Inspector

When I run the game, Unity's inspector updates every few seconds, but totalTimeElapsed only increments in a series of 0.1 seconds. I tried a similar thing with totalTimeElapsed += Time.deltaTime but with the same result.
How do I get the total time since game start?
float totalTimeELapsed = 0;
int dayCount = 0;
private void Update()
{
totalTimeElapsed = Time.time;
dayCount += 1;
AFunctionTakingLotsOfTime();
}
Because AFunctionTakingLotsOfTime() takes a long time, you need Time.realtimeSinceStartup
Time.realtimeSinceStartup deals in real time, rather than computed frame-rate-time amounts that are probably getting messed up due to the fact that your function takes a long time.
Alternatively you could use DateTime.
Note that both will not be affected by things like time scale and you may want to change your function to be less intensive, such as computing only a small chunk any given call (or by using a coroutine) or by splitting the method off onto a Job thread.
float totalTime = 0f;
// Update is called once per frame
void Update () {
totalTime += Time.deltaTime;
}
I believe what you are looking for would be something like this. Assuming this class is present on runtime of your game, you should have an accurate variable which can tell you the elapsed time since the game started.
You might also want to know that the update call occurs once per frame, ie. if you are running at 60 FPS update will be called 60 times in that second. You can try FixedUpdate which updates in real-time intervals instead.

Coroutine is causing uneven spacing between objects

I have a Coroutine, and I have tried using WaitForSecondsRealtime and WaitForSeconds. When I run the Coroutine, the delay is the same for a few iterations, but then there is a gap where the delay is a little longer, then it goes back, then there is a delay again.
Here is an image of what is happening, as you can see there are a few gaps between circles, and I would like the gaps to be a bit more constant.
Here is the Coroutine that I am using for this:
public float shootDelay = 0.03f;
IEnumerator Shoot(Vector3 direction) {
direction.z = 0f;
foreach (GameObject ball in balls) {
float speed = ball.GetComponent<Ball>().speed;
ball.GetComponent<Ball>().rb.velocity = direction.normalized * speed;
yield return new WaitForSecondsRealtime(shootDelay);
}
}
It is hard to tell from your tiny bit of code exactly what your question is. A good Minimal, Complete, and Verifiable code example would help a lot (perhaps minus the standard Unity3d boilerplate…there's an art to providing just the right amount of code for a Unity3d question).
But is seems like you are trying to implement a delay between "balls" being "shot" from something. As such, you want for each ball to be initialized at precise intervals. They are not, which results in irregular intervals.
Unfortunately, I don't think you can count on the WaitForSecondsRealtime() (or WaitForSeconds(), for that matter…which is probably a better choice here, as it would automatically adjust if you change the game simulation speed). These functions have significant sources of imprecision that you will need to take into account, particularly that the time that's waited is counted only after the current frame is entirely complete and your coroutine is only resumed at the first frame after the specified time has elapsed.
One way to take into account this imprecision is by using the Time.time value to track how much simulation time has actually passed since you started firing the bullets. Initialize a variable in the coroutine just before the loop, to the current value. Then when you are setting the velocity of the bullet object, also set its position based on the velocity and the difference between the current time value and that bullet's intended time value. Then wait for a period of time not the exact interval you want, but an interval that represents the difference between the current time and the next bullet time.
Without a good MCVE it's not practical for me to try to test this (i.e., I haven't), but something like this should work:
IEnumerator Shoot(Vector3 direction) {
direction.z = 0f;
float nextBulletTime = Time.time;
foreach (GameObject ball in balls) {
Ball ballComponent = ball.GetComponent<Ball>();
float speed = ballComponent.speed;
Rigidbody rb = ballComponent.rb;
rb.velocity = direction.normalized * speed;
rb.position += rb.velocity * (Time.time - nextBulletTime);
nextBulletTime += shootDelay;
yield return new WaitForSeconds(nextBulletTime - Time.time);
}
}

Unity3D - Using Time.deltaTime as wait time for a coroutine

TL,DR: is it safe to use Time.deltaTime as the wait time for the yield in a coroutine?
Often to avoid unnecessary logic inside Update() for short lived functionality I'll run a coroutine instead. For example in my game I have a slow coroutine that periodically checks the distance between NPCs and the player, if that distance is under some threshold I'll then run a more frequent coroutine with more expensive line of sight testing using raycasting etc. This seems much more efficient than checking everything constantly in Update().
My question is this: is it safe to use Time.deltaTime as the wait time for the yield, thereby simulating Update() for the duration of the coroutine? i.e. yield return new WaitForSeconds(Time.deltaTime);
Since deltaTime is not constant what happens if the next frame takes longer than the last - is the coroutine delayed until the next frame, or is something horrible happening like the whole frame being delayed until the coroutine finishes? I'm tempted to allow a buffer for safety e.g. yield return new WaitForSeconds(Time.deltaTime * 1.2f); but ideally I'd like it to execute precisely every frame.
is it safe to use Time.deltaTime as the wait time for the yield in a
coroutine?
No. That's not how to use the WaitForSeconds function. WaitForSeconds takes in seconds as a parameter not the tiny values provided by Time.deltaTime in every frame.
Below is an example of how to use the WaitForSeconds function.
IEnumerator waitFunction1()
{
Debug.Log("Hello Before Waiting");
yield return new WaitForSeconds(3); //Will wait for 3 seconds then run the code below
Debug.Log("Hello After waiting for 3 seconds");
}
As for waiting with Time.deltaTime, you usually use it in a while loop in addition to another float variable you will increment or decrement until you reach the wanted value.The advantage of using Time.deltaTime is that you can see how much waiting time is left while waiting. You can use that for a countdown or up timer. You also put yield return null; in the while loop so that Unity will allow other scripts to run too and your App won't freeze. Below is an example of how to use Time.deltaTime to wait for 3 seconds. You can easily turn it into a countdown timer.
IEnumerator waitFunction2()
{
const float waitTime = 3f;
float counter = 0f;
Debug.Log("Hello Before Waiting");
while (counter < waitTime)
{
Debug.Log("Current WaitTime: " + counter);
counter += Time.deltaTime;
yield return null; //Don't freeze Unity
}
Debug.Log("Hello After waiting for 3 seconds");
}
If you want to check every frame there are WaitForEndOfFrame
There is no point to WaitForSeconds if you don't need specific time to wait for
You can also yield return www to wait for it finish
Update, there are CustomYieldInstruction and some of it inheritance for used in coroutine, like WaitUntil
I know that this is a bit old, but I just came across it today while looking for something that's related.
Anyways, Programmer's answer is a good start. Using Time.detlaTime is not a good way to release control of a coroutine. For one, using a null value will get you to the next frame in simulation. Second, what happens if the frame you just finished was a bit slow (or the next frame is a bit fast). Please keep in mind that the simulation (or game) runs in frames, much like the frames in a film. Because of this, using WaitForSeconds can have some interesting effects. For instance, if the time you requested happens between frames, what do you think will happen? The coroutine will regain control after the requested time. So, if you were hoping to just shoot off a WaitForSeconds(Time.deltaTime) on each loop in the coroutine, chances are, you are skipping the occasional frame (sometimes more).
I had a coworker that did a bunch of WaitForSeconds, inside a long running coroutine and the total time that passed was anywhere between 5 to 10 seconds longer than what was expected. Those off by part of a frame errors in the requested time will add up, and unlike nature, where errors tend to average out, these errors will always add up. That's because the amount of time that passes will always be greater-than or equal-to the amount of time that's requested.
Programmer's answer showed you how to use Time.deltaTime to get the passage of simulation time. I should warn you that once you start using things other than null to release control of the coroutine, that Time.deltaTime might stop working for you.
If you use a null follow by some code followed by a WaitForEndFrame, you will still be within the same frame when the coroutine resumes control. Because you are still in the same frame as you were before calling WaitForEndFrame, the simulation time that has passed will be zero, but Time.deltaTime will not be zero. So, yeah, measuring time can get tricky once you start mixing different return types. You should also be careful of switching between frames and fixed-frames. Using a WaitForFixedUpdate will put you into the times of the fixed-frames. Calling Time.fixedDeltaTime after a WaitForFixedUdpate will give you the time between that fixed-frame and the previous fixed-frame, which has nothing to do with Time.deltaTime or any of timings for the frames.

Categories