Search Unity

FixedUpdate being called when Time.timeScale == 0.0f, causing extra physics step

Discussion in 'Scripting' started by Deibu, Jun 26, 2017.

  1. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    Hello,

    I am running into an issue where FixedUpdate is being called when Time.timeScale is 0.0f. This is also causing the physics step to advance by an additional step, causing my objects to move twice as fast for that FixedUpdate. What might be causing this and is there a way to ensure my objects do not move if Time.timeScale is set to 0.0f?

    Thanks.
     
    Last edited: Jun 26, 2017
  2. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Multiply your speed or force by Time.deltaTime, or Time.timeScale. That way if it's 0, even if the code runs nothing will move.
     
  3. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    I am not manually adding any forces in any update with my objects. I am only using gravity, constant velocity, and animators that are set to animate with physics. Further, I tried an empty project and don't have anything moving in my scene and I still am running into the issue where FixedUpdate is being called when Time.timeScale is 0.0f.
     
  4. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    The docs advise that when you reduce TimeScale, you also reduce FixedDeltaTime.
    Code (CSharp):
    1. Time.fixedDeltaTime = 0.02F * Time.timeScale;
    https://docs.unity3d.com/ScriptReference/Time-timeScale.html

    You could give that a shot. I'm not sure why you're seeing an extra call to FixedUpdate though.
     
    andrenospam0 likes this.
  5. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    Because I set Time.timeScale to 0.0f, if I set Time.fixedDeltaTime to 0.02f * Time.timeScale then it makes an even greater amount of extra calls to FixedUpdate. If I set Time.fixedDeltaTime to float.MaxValue when Time.timeScale is 0.0f, it helps a little bit, but it eventually performs and extra call to FixedUpdate.

    Edit: Also, I am only setting Time.timeScale to 1.0f or 0.0f.
     
  6. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    When you see this extra call to FixedUpdate, what is your Time.time value?

    If you happen to stop exactly on a FixedUpdate time (e.g. divisible by 0.02), I would expect there to be one more FixedUpdate call. That said, I'd be shocked if this happened consistently, unless you're setting Time.timeScale to 0 in the first frame, when it's 0 to begin with.
     
  7. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    In the project that is empty, it is almost always divisible by 0.02, or very close to that. However, is my project it is not divisible boy 0.02. In one case, Time.time was 24.32667.

    I am setting Time.timeScale to 0.0f every 3rd (or n-th) physics step.
     
  8. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    If you're only setting Time.timeScale in FixedUpdate, you should try setting it in the normal Update step or other non-fixed functions. FixedUpdate can be called multiple times in the same frame, and I don't know what kind of behavior you can expect from setting timeScale during a FixedUpdate. Setting it outside FixedUpdate will take effect for the next frame's FixedUpdates, which in theory should not happen if timeScale==0.
     
    StarManta likes this.
  9. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    I set Time.timeScale to 0.0f in FixedUpdate and then reset it back to 1.0f in Update or at the end of the frame. I am essentially trying to freeze time in my FixedUpdate and then let the regular Update resume time after a given event has happened.
     
  10. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    +1 to @jeffreyschoch 's advice, which helped bring the problem into focus for me - I'm pretty sure I know what's going on.

    Start by looking at the flowchart of execution - in particular, the FixedUpdate part of it.

    I think what's happening here is this:
    Unity starts a game frame. The "real time" value is 0.085234235 seconds.
    Unity runs FixedUpdate repeatedly until it catches up to real time. It runs FixedUpdate for 0.00, 0.02, 0.04, 0.06, and 0.08.

    On the third of these FixedUpdates, you're setting Time.timeScale to 0. But, it's still not caught up to "real time". So even though "real time" is not advancing, physics time still has to catch up, and it will run FixedUpdate (and all physics) two more times (for the 0.06 and 0.08). These two are what's causing the issues you're seeing.
     
    LiterallyJeff likes this.
  11. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    You may try increasing the fixed time step (to an arbitrarily large value, like 999) instead of decreasing it. This may make the engine think it has caught up to real time with its FixedUpdate's. However, it's possible this may not work depending on the details of how Unity coded this loop.

    This is a bit of a dirty hack. It might be helpful to ask what your goal is? I ask because this seems really unusual to me:
     
  12. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    I am essentially trying to output various camera snapshots at a constant frame rate. Because of the amount of data I want to output, I cannot do it in real time, so I wanted to freeze time every several physics updates to ensure that my objects are at their correct positions until the next frame is rendered.
     
  13. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    I suppose another dirty hack is to add a "if (Time.timeScale == 0) return;" at the beginning of FixedUpdate, but like the above posters noted, it seems like you are breaking Unity when trying to change the time scale in FixedUpdate anyway.
     
  14. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    This will not work for regular physics objects or animators.
     
  15. Midnight-Dev

    Midnight-Dev

    Joined:
    Dec 18, 2015
    Posts:
    1
    StarManta likes this.
  16. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    I believe I was just looking for Time.captureFramerate.
     
    Last edited: Jun 26, 2017
  17. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    Nevermind... Time.captureFramerate is trying to do what I was attempting with freezing time in FixedUpdate. Objects are still not moving at a constant velocity. For an object with a constant velocity of 5.0 m/s, in some frames, it is moving at 6.0 m/s and other frames at 3.0 m/s and 0.0 m/s. I need to ensure that the velocity between frames is accurate. Not sure how to resolve this now...
     
    Last edited: Jun 26, 2017
  18. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    The only solution I can think of right now is... When FixedUpdate is called an extra time, record snapshots of every single moving object in the scene and then restore each object to its snapshot state in WaitForFixedUpdate.
     
  19. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    You want FixedUpdate to be called as many times as it needs to. It's an incremental function, so it won't ever go further than it's supposed to based on the amount of time that has passed since last frame. The "amount of time passed" or deltaTime is affect by TimeScale.

    You're trying to take screenshots according to your previous reply? I'm not even sure you need to stop time for this.
    Code (CSharp):
    1. public class Example : MonoBehaviour
    2. {
    3.     public float snapshotDelay;
    4.     public string filePath;
    5.  
    6.     private WaitForSeconds snapshot;
    7.  
    8.     private void Awake()
    9.     {
    10.         snapshot = new WaitForSeconds(snapshotDelay);
    11.     }
    12.  
    13.     private IEnumerator Start()
    14.     {
    15.         while(enabled)
    16.         {
    17.             Application.CaptureScreenshot(filePath);
    18.             yield return snapshot;
    19.         }
    20.     }
    21. }
     
    Last edited: Jun 27, 2017
  20. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    Well, I am trying to output the image from an array of cameras, including depth image, motion vectors, etc., as well as outputting text data containing object poses and velocities.

    I tried a simple test where I have my object's velocity set at 5.0 m/s and I take screenshots every 1/30 seconds. When I record the velocity at each screenshot, it varies between 3.0 m/s and 6.0 m/s, and is never exactly 5.0 m/s. Also, if I don't follow my solution above, FixedUpdate gets called an additional times trying to catch up to what Unity thinks the next Time.time value should be and ultimately moves my object further than I would expect it to. An example of this is when I tell my object to stop moving after 1 second. FixedUpdate ends up thinking that it needs to catch up to some time value that is past 1 second. The result of this is that my object moved over 6 meters, instead of 5 which it should have moved based on it's velocity and the time I want it to stop at.
     
  21. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    I'm pretty certain all the issues you're seeing are a result of trying to set TimeScale in a FixedUpdate.

    If you want to stop time, try to do it in the normal Update execution step. The following frame's FixedUpdates will not execute. Don't try to interrupt the physics update from within the physics update.

    Physics updates first, then the normal frame update happens, so try this:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Test : MonoBehaviour
    4. {
    5.     private void Update()
    6.     {
    7.         if(Input.GetKeyDown(KeyCode.Space))
    8.         {
    9.             Time.timeScale = Time.timeScale == 0 ? 1 : 0;
    10.             if(Time.timeScale == 0)
    11.             {
    12.                 Debug.LogFormat("[Update] Time Stopped at: {0}", Time.time);
    13.             }
    14.         }
    15.     }
    16.  
    17.     private void FixedUpdate()
    18.     {
    19.         Debug.LogFormat("[FixedUpdate] Current Time: {0}", Time.time);
    20.     }
    21. }
    If you run that and press Spacebar, you'll see that Update will print the final time stamp, and physics will still be frozen at the correct state from the frame you stopped time.

    So you should be able to stop time whenever you want in the normal frame update, do all your image processing and formatting etc that may take multiple frames, then resume time.

    I've used time stoppage in the past as a sort of frame-stepper as an in-game screenshot feature, with physics bodies in the scene working properly at each step.
     
    Last edited: Jun 27, 2017
  22. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    I'm going to repeat others here. Don't change the Timescale inside a FixedUpdate, or at the very least don't expect that change to take effect until the next frame.

    Unity likely doesn't constantly check the Time while it runs its FixedUpdate. more likely unity figures it all out once per frame, calculates how many times it needs to run FixedUpdate that frame, caches those values within scope, and then performs that for all objects.

    so for example it may figure that it needs to run FixedUpdate 12 times this frame, so even if you've set TimeScale to 0 inside the 1st fixedUpdate, it'll still run 11 more times, because Unity already planned to run 12 times this frame. You may not like fact that its not as dynamic, but it makes it very stable (cause a ton of things are dependent on Time).
     
  23. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    Pausing time in regular Update does not fix the problem.

    Let's say I want to capture a frame after exactly 1 second and there is an object moving at 5.0 m/s. If we pause time in Update, there is no way to ensure that the object moved exactly 5.0 meters. If FixedUpdate happens 50 times per second, then this would require that it runs exactly 50 times before we capture the frame. However, if Unity determined that FixedUpdate needed to run 52 times, then our object would have moved 5.2 meters.

    We need to be able to pause the physics simulation on an exact step in its calculation, otherwise we can never ensure that our object moved by an exact amount.
     
  24. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    couple misconceptions:

    1.
    You can't do anything at an exact point in time inside the Unity thread, only an approximate point. and while you could do something more accurate in another thread you'd still have to wait for the Unity thread. For example, doing something like WatForSeconds(0.00001f) doesn't actually cause a coroutine to wait exactly 0.00001 seconds... nope it just waits for the first upcoming frame that passes that delay, usually around 0.1~0.3 secs later. And you cannot rely on a stable framerate, it will always fluctuate. Thats the entire reason Time.deltaTime even exists, so that you can compensate for it.

    2.
    First off FixedUpdate is frame-dependent, not seconds-dependent and realistically it wouldn't run exactly 50 times a second, but upto 50 times a second. Because, again, its gated by frames not seconds.

    I don't think you really need to pause on the exact physics step, but simply snapshot on the exact physics timestep.

    first is to set up your own egg timer that counts to the fated FixedUpdate (sounds like you already have something in the works), then snapshot the transform vslues for all active physics objects you want recorded (or record the current animation's normalized time, whichever is relevant). Then raise a flag for that frame so that in LateUpdate you can first backup the current state, then set the transforms for the snapshot then in the next Update revert back to the saved state.
     
  25. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    I don't think that there are any misconceptions at all.

    1.
    Because Update is never ran at an exact point is time is precisely why I want to freeze time in FixedUpdate, since it is running at a fixed rate.

    2.
    50 times in a second was an example. My point was that Time.time would likely be over 1 second, causing FixedUpdate to run more than 50 times.

    3.
    I have a working solution (what I mentioned earlier) where I essentially am pausing time on the exact physics step that I want to take a snapshot of. After I record the image in WaitForEndOfFrame, I resume time again and all is well.
     
    Last edited: Jun 28, 2017
  26. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Maybe what you want to do is manually simulate your scene using Physics.Simulate and not rely on stopping mid-physics update, which really isn't a great way to work.

    Being able to simulate to exactly 1 second is very unlikely using the automatic physics sim without breaking something in the process, or getting unexpected behavior as you've discovered. Your aggregate deltaTime will never reach 1 second exactly, and FixedUpdates are supposed to catch up to the real deltaTime in increments of fixed timestep. Interrupting it makes it go out of sync, so it will always try to call FixedUpdate the correct number of times.
     
  27. Deibu

    Deibu

    Joined:
    Feb 24, 2013
    Posts:
    34
    Good find. Didn't notice that since it only appears in the 2017.1b script reference and does not exist prior to 2017. Hopefully the upgrade to 2017 will work and not break too much stuff.
     
  28. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Yes it is new for the 2017 beta I believe.

    There's a good thread about it here, regarding 2D physics but the API should have parity for 3D physics:
    https://forum.unity3d.com/threads/2...ion-simulation-groups-multiple-worlds.466501/