Search Unity

Discussion I'm completely unable to set a frame-INDEPENDENT reliable timer

Discussion in 'Scripting' started by GuirieSanchez, Jul 30, 2022.

  1. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    I'm using a timer for the jump (to have a min. jump and a maximum jump), and I used a timer with a
    Time.deltaTime
    that is pretty consistent (with just 0.001~0.00.8 range difference, which is indeed small) but just for a consistent framerate.

    Let's say that when the game is running at 300fps, the whole duration of a full jump is 1 second, and the maximum height of the jump is 2m. Now, if I turn on the V-sync so that I can have 60fps, then the full jump duration goes (for instance) to 1.2s and the maximum jump height is 2.3m-2.4m.

    On a second attempt, I tried using
    Time.fixedDeltaTime
    either in
    FixedUpdate()
    and
    Updtate()
    which should be more reliable even with frame rate changes; however, even if the maximum jump is more consistent, there are still some inaccuracies: At a consistent framerate (let's say 60fps with the v-sync on) sometimes the full jump gives me 1.18s while other times it gives me 1.20s (and in rare occasions, it throws a 1.16s).
     
    Last edited: Jul 30, 2022
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    First, understand that fixed step is intended for a completely separate time track, used for physics simulation, which runs on a separate thread. Mixing the two time tracks without having thorough understanding of the system will always yield unpredictable results.

    Fixed step is also configurable and Time.fixedDeltaTime only reflects that and will likely stay constant for the most part. Time.deltaTime is a true measure of frame by frame performance and cannot possibly lie. But even though it cannot lie, it is always exactly one frame behind, which is a physical reality of any real time framerate system. I know this sounds idiotic but there is always a current frame being processed, which will finish processing once it's finished processing.

    Consider too that there are two different MonoBehaviour "hooks" for the underlying engine where you can process your data step-by-step: Update and FixedUpdate. While FixedUpdate will be called every time a physics step is processed, Update will be called every time a rendering engine is ready to begin processing a new frame. Just keep in mind that there are no guarantees that the two methods will be called in sync even if they should (and they shouldn't, physics simulation should tick at a slower rate than graphics rendering).

    Now, to make things frame-independent, you want to make a model reliant on some term t which is continuous and representative of real time passage. This means t should always be correct and reliable in your environment regardless of whether the software slows down or speeds up, stutters etc. To get such a value reliably you can simply accumulate Time.deltaTime with each passing frame, but in a common practice, this accumulation is useless so you simply use the measure of frame time ad hoc in order to convert from i.e. velocities expressed in m/s to velocities expressed in m/f where f might be of variable duration. This is done by simply multiplying the desired velocity with Time.deltaTime, and so while it certainly looks trivial in other people's code, all things considered it's not, as you need to appreciate why this is done in the first place.

    Finally, if you don't concern yourself with the physics simulation, you simply don't use anything 'fixed'. You need Time.fixedDeltaTime only inside FixedUpdate which isn't something you should casually use as a substitute for Update.
     
    Nefisto likes this.
  3. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    The
    Time.deltaTime
    value, as I mentioned, is inconsistent (or rather frame-dependent). The only reason you can rely on
    Time.deltaTime
    to calculate velocities is because you're getting a constant value for any specific frame rate at any given time. For instance, this quote by gamedevplanet:
    So we can rely on
    Time.deltaTime
    for velocity calculation, right. However, my problem remains: I cannot get an accurate timer, as I explained before when I was using
    Time.deltaTime
    , since this value is frame-dependent.
    In theory, it should be possible. I just don't know how. Let me give you a basic implementation so that you can tell me what I'm doing wrong ↓
     
  4. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Code (CSharp):
    1.  
    2.     private float _maxJumpTime = 0.6f;
    3.     private float _jumpTimer;
    4.  
    5.     //(JUMP INPUT (Jump is Pressed)
    6.     JumpInput = true;
    7.     _jumpTimer = 0f;
    8.  
    9.     private void Update()
    10.     {
    11.         Jump();
    12.     }
    13.     private void Jump()
    14.     {
    15.         if (!JumpInput) return;
    16.         _jumpTimer += Time.deltaTime;
    17.         Debug.Log(_jumpTimer);
    18.  
    19.         if (_jumpTimer > _maxJumpTime)
    20.          {
    21.             JumpInput = false;    
    22.             StopJump(); //this stops the jump
    23.          }
    24.  
    25.     }
    26.  
     
    Last edited: Jul 30, 2022
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    It is a value intended to let your code only advance by an amount equal to how much time has passed.

    Properly used it lets you write framerate-irrelevant code.

    If you put this (with
    private float now;
    ):

    Code (csharp):
    1. now += Time.deltaTime;
    in an Update() loop, it will increase by a total of 1.0 every time 1.0 seconds goes by, scaled by the time scale in effect, regardless of frame rate, to the guard-bounds limit specified in Time settings.
     
  6. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Why am I getting inconsistent results with the above code then?
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    You're gonna need to define that word.

    Will each frame on each run be the same length? NO.

    Will the aggregate advancement of time over a given time be consisten? YES, within the bounds outlined in Time settings and the precision of a single frame.

    If it appears not to be, that's a bug that is certainly on your side. We know Time.deltaTime works.
     
  8. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    This is exactly why I'm asking for help. I don't doubt the efficacy of
    Time.deltaTime
    , I doubt the efficacy of my implementation that is indeed giving me inconsistent results. (inconsistent defined in my first post ↓↓)

     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    Jumping should not have anything to do with time: gravity takes care of it as it integrates over time, bringing you back down ballistically.

    Do you mean that you are using Time and holding a key to decide how much to jump? That won't be consistent if you are adding a constant amount per frame. Instead, add a force proportional to Time.deltaTime each frame.

    BTW, the code you posted above just unconditionally tallies Time.deltaTime per frame, so it cannot possibly have a bug. There is also no jumping (velocity upward) going on there, despite the names of the variables and functions.
     
  10. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    If you're having a constant framerate of 60, Time.deltaTime is expected to be 1/60 plus/minus some epsilon.
    If you're having a constant framerate of 300, Time.deltaTime is expected to be 1/300 plus/minus some epsilon.

    This epsilon value must exist due to three things: slips in hardware (oscillators, cache misses, hardware clock misalignment, whathaveyou ...), numerical representation inaccuracies (single-precision floating point errors), instructional discrepancies (in terms of how well the actual measurement is processed and stored by the underlying C++ engine). All three are so miniscule that even accumulating deltaTime will work over sufficiently long time intervals, without noticeable cumulative errors. I'd say days before a slip becomes relevant, but I cannot vouch for this without writing proper tests (I never had to) -- in any case you can very easily verify this for your own case scenario. In other words, such an epsilon can mostly be ignored for practical (non-scientific) uses.

    Finally, even the epsilon can be completely ignored if you simply bypass accumulation and treat it as piecewise incrementation, which ane frame-by-frame system (like a video game) presupposes. Nobody will ever notice an error at an order of 1E-10, and this should be the case for your program as well.

    As stated above, we trust Time.deltaTime, and because there is no reason not to trust it (so far, and btw it is very easy to implement a time measure of your own), you can use this knowledge to track down the issue you're having with your own code. deltaTime has to be the most used, and abused thing in Unity, in each an every game that has been made in the last 15 years.
     
    Kurt-Dekker likes this.
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    Also, if you are doing ANYTHING with physics and you are doing it in Update() you have already failed to follow the instructions for using physics.

    Always do ALL your physics-facing stuff in FixedUpdate().

    Here is some timing diagram help:

    https://docs.unity3d.com/Manual/ExecutionOrder.html

    Two good discussions on Update() vs FixedUpdate() timing:

    https://jacksondunstan.com/articles/4824

    https://johnaustin.io/articles/2019/fix-your-unity-timestep
     
  12. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    No, it's just a one-time velocity added. I didn't put it in the code because I wanted to make sure that the implementation of the
    Time.deltaTime
    there was correct.
    The jump code does the following:

    (1) In the first frame:

    (1.1) adds the velocity (one time)
    (1.2) removes gravity (one time)


    (2) Update:

    (2.1) Checks if you have released the button, and turns the
    jumpButtonPress
    parameter to false.
    (2.2) If the
    jumpButtonPress
    is false and the
    jumpTimer
    is greater than the
    minJumpTime
    then the velocity gets cut and a higher gravity gets implemented, therefore the player falls back down.
    (2.3) Here it checks the apex of the jump with a parameter
    jumpApex
    and sets gravity to the regular amount
    (2.4) Checks if the
    jumpTimer
    is greater than the
    maxJumpTime
    then the velocity gets cut and a higher gravity gets implemented.


    PS: The code works correctly. By using the timer (implemented in the above code), I'm experimenting different heights when the framerate changes (now I'm comparing 800fps to 60fps).
     
  13. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    Okay, you lost me man... I thought we're talking about basic Newtonian dynamics (simple physics) the way it works in real life, the way it is discretely simulated in any digital simulator.

    If you're jacking around with gravity during the arc of a jump simulation, ESPECIALLY if you are doing so in Update(), all bets are OFF.
     
    GuirieSanchez likes this.
  14. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    wait
    Do you use physics or not? I.e. do you have RigidBodies on your objects?
    What does "adds the velocity" mean? You add it to what? And this velocity measures what exactly?
    Why do you remove gravity?

    The second part suggests to me that you're clearly underestimating the implications of what happens in different framerates.
     
    GuirieSanchez likes this.
  15. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Glad to know the issue was there. It didn't even cross my mind that could be a possibility. I'll have to think about a way around it.
     
  16. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    Imho, you are asserting that a problem lies elsewhere, whereas you should've asked for a snippet of a solution that does X, instead of asking about a problem with Y, which is completely misconstrued. You are doing things wrong from the get go is my intuition.
     
  17. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Yes, I'm using a RigidBody2D, and I'm using this formula to jump:


    Code (CSharp):
    1. public void SetVelocityY(float velocity)
    2.     {
    3.         _workspace.Set(CurrentVelocity.x, velocity); //velocity = 20f / workspace is a vector 2
    4.     }
     
  18. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    That's not a formula. You are accepting a Y-velocity parameter and setting it into some other API.

    This is the actual formula for jumping:

    Code (csharp):
    1.     void Update ()
    2.     {
    3.         if (Input.GetKeyDown( KeyCode.Space) ||
    4.             Input.GetMouseButtonDown(0) )
    5.         {
    6.             float height = 6.0f;
    7.  
    8.             float velocity = Mathf.Sqrt( Physics.gravity.magnitude * 2 * height);
    9.  
    10.             GetComponent<Rigidbody>().velocity = Vector3.up * velocity;
    11.         }
    12.     }
    Line 8, taken straight from classic Newtonian dynamics / physics.

    This stuff is pretty well-studied and understood... we have placed men on the moon after all!

    Screen Shot 2022-07-30 at 7.24.41 AM.png
     
    GuirieSanchez and orionsyndrome like this.
  19. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    In the end, this is your problem, and why it sounds so confusing from every angle: you're basically tweaking the physics parameters on the fly, then using Update to fiddle with it even more, then you think what you've gotten has something to do with the passage of time, and not that you're confusing the underlying computation entirely. (I.e. you can't just switch off gravity and expect wonders, it's a fundamental force acting on your jump, and you need to let it do its own integration at its own pace, otherwise don't use integrated physics, and write your own.)

    Hopefully, you will invest some more time in trying to understand how physics simulation is supposed to be used effectively and reliably. Consider also what I said in my first post that anything physics-related has nothing to do with frame rendering, and thus Update and deltaTime have almost nothing to do with it. I say almost because people do like to do input processing in Update, simply to maintain a better input responsiveness of their game.
     
  20. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    My apologies:

    Code (CSharp):
    1. public void SetVelocityY(float velocity)
    2.     {
    3.         _workspace.Set(CurrentVelocity.x, velocity);
    4.         RB.velocity = _workspace;
    5.         CurrentVelocity = _workspace;
    6.     }
    And the formula for velocity is the one you wrote.

    But let's remove "gravity" from the code. I'm just using the timer to detect the peak of the jump, for instance, it will be 0.82f (and remove all else, I'm just interested in the printed time):

    (1) I start jumping and set
    jumpFinished = false;

    (2) ↓↓
    Code (CSharp):
    1.  
    2. private void JumpTEST()
    3.     {
    4.         if (jumpFinished) return;
    5.         Debug.Log(_jumpTimeCounter);
    6.         if (_jumpTimeCounter > 0.82f)
    7.         {
    8.             jumpFinished = true;
    9.         }
    10.     }
    with 60fps, the value printed is 0.821 ~ 0.842 (most of the time is 0.83 something)
    with 700~800fps, the value printed is 0.820 ~ 0.821 (most of the time is 0.820)

    the sample is small, 20 times each, but still there's a difference.
     
  21. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    I don't see how I'm asserting anything. The question is about having a reliable frame-independent timer and my struggles in trying to implement it. I didn't mean to assert anything, in any case, it was just my best guess based on the values I was getting printed.

    EDIT: in hindsight, I shouldn't've mentioned anything about my jump, because now the original question has been completely dismissed.

    In fact, gravity, or the jump force, or the whole jump formula here should not affect the timer. There's still inconsistencies in the jump height when removing the gravity out of the equation.

    So, to test the timer I just press a button and receive feedback after X time has passed.
     
    Last edited: Jul 30, 2022
  22. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    [A quick disclaimer: @orionsyndrome you don't have to be rude, you're not forced to reply or help if you don't want to. There're people starting to learn now and so there will be in the future, no need to be that categorical and harsh IMO.]

    My problem remains since I can't rely on this timer (either for printing or for stopping the jump), and this is the line that is giving me problems:

    With my previous jump (modifying gravity), with high framerates (700~800) I was getting pretty consistent results, and I had 0 problems with it until I switch V-sync on. And yes, I was modifying gravity and I tested it yesterday for 5 hours and I got literally 0 problems. When I print the timer with 700~800fps I'm also getting pretty consistent values
    As you can see above, a 0.001s fluctuation at most.
    Now I switch to a Newtonian jump (just giving the RigidBody2D an impulse and letting gravity do its job, without me modifying or tweaking anything). Guess what, the results are the same: pretty consistent print values and pretty consistent jump. When I set the timer for a short jump it works perfectly, it doesn't feel off any single time.
    This is why I didn't notice any problems yesterday.

    Now, switching V-sync on and testing with 60fps:
    Even with the Newtonian jump, the short jump height changes a lot because (this is my best guess) the timer values are off by a lot (more than 0.02s fluctuation), and the results are very noticeable. Imagine if a set the short jump for a 0.1f value, and I'm spamming the jump button: the character jumps and falls tracing inconsistent arcs (remind you that I'm not touching gravity) because the jump duration is sometimes 0.089, some other times 0.119, etc. You may think is not that big of a deal but it feels completely off. (And again, this doesn't happen at 700~800fps where the arc is the same and the feeling is right).

    Any help or guidance towards solving the issue is appreciated. If, again, it's nothing to do with the timer and I'm missing something else, also any help/guidance is most highly appreciated.
     
  23. Gotmachine

    Gotmachine

    Joined:
    Sep 26, 2019
    Posts:
    37
    First, non-determism and floating point precision mean that you can't rely on an exact duration, especially for something that happen over the course of many frames. Whatever you're trying to do, you're doing it wrong, and there is very likely a better way to achieve the end goal.

    Second, I guess you're still calling this from
    Update()
    and using
    Time.deltaTime
    . As others have pointed out, that mean the minimum precision will be framerate dependent, and since framerate is always variable, you will never obtain a consistent value. Basically, you have a timer whose precision can't be less than 1/framerate.

    Last, even if you were doing this in
    FixedUpdate()
    and using
    Time.fixedDeltaTime
    , due to physics (and even more importantly, your starting conditions) being non-deterministic, you can still have a variance of one or even more fixed frame(s), ie multiples of 0.02s (assuming you have physics running at the default 50hz).

    Edit : re-read the thread quickly, and it seems that your actual issue is seeing inconsistent behavior when jumping. It's not entirely clear what you're doing, but in case you're setting the RB velocity directly, that's likely where your issue is, and you should use the
    AddForce()
    methods instead.
     
    Last edited: Jul 30, 2022
    GuirieSanchez likes this.
  24. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Thank you for the feedback.

    Therefore my question. Despite the fact that Time.deltaTime was advised, the fewer framerates the less accurate it gets, to the point of being quite inconsistent at 60fps.

    I'm open to alternatives (even if they do not necessarily implement a timer) as long as the end result is the same. I wonder how one would go about and implementing, for instance, a knockback of 0.1s that is consistent across framerates.
     
  25. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    This is unlikely going to solve my issue because not only I have tried that before and still got the same problems, but most importantly, I don't think it's the root cause of the issue. If I were to stop the jump by having these 2 conditions (button UP and
    jumpTime
    >
    shortJumpTime
    ), it doesn't matter whether I applied force, velocity, or transform the position, because all comes down to the second condition (for a minimum jump time) not being accurate: sometimes it fires off earlier than it should, sometimes later than it should when having 60fps.

    Also, I would like to mention that modifying gravity and cutting off a jump are well-known practices in 2D platformers (for instance, hollow knight), and they have proven to work reliably. Another thing will be discussing about the methods they used or how they accomplish certain things. As far as I know, in hollow knight they used rigidbody2D, they tweaked gravity, they have a minimum jump time, and you can stop jumping immediately by letting go of the button and meeting the
    shortJumpTime
    condition.

    The last thing, I think it's important to have reliable timers for things other than a jump (knockbacks, or showing something on a screen after X time, etc.)
     
  26. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    What does this mean? You only move back for 0.1 seconds? That's tiny! Barely a hiccup! Animate it yourself with a hard-coded animator, you'll see what I mean.

    Or do you mean accelerate backwards for 0.1 seconds, then coast until your backward knockback velocity returns to normal? Then it matters: how much are you accelerating?

    Usually a knockback is simply a fixed displacement added to your position for a short period of time. Use an AnimationCurve to specify it and you can sculpt the onset and fade just right. Combine that with leaning the player back like he got smashed in the face and the illusion is complete.

    What does this mean? What is a minimum jump time? A minimum time between making jumps? A minimum velocity that all jumps will get to cause you to remain airborne for a minimum amount of time?

    WHY?!!! The peak of the jump occurs between the frame when you are NOT going down and the frame when you ARE going down.

    That has absolutely nothing to do with time and it is trivial to compute by observation:

    Code (csharp):
    1. private float previousFrameYVelocity;
    and then in FixedUpdate():

    Code (csharp):
    1. // this takes it off your Rigidbody, if you're doing something else to get
    2. // velocity, obviously replace this line with something that gets your y velocity.
    3. float yVelocity = myRigidbody.velocity.y;
    4.  
    5. // WERE you NOT going down last frame?
    6. if (previousFrameYVelocity >= 0)
    7. {
    8.   // and ARE you going down this frame?
    9.   if (yVelocity < 0)
    10.   {
    11.     // Then you have peaked!
    12.     Debug.Log( "Peak of Jump has happened!");
    13.   }
    14. }
    15.  
    16. previousFrameYVelocity = yVelocity;
    Note that it has nothing to do with time. Peak of jump only cares about velocity, not time.
     
  27. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    I am sorry, why am I rude?

    In my mind, this reply of yours only serves as a proof that you seemingly come to wrong conclusions time and time again, making things look much more complicated than they truly are.

    But in any case, I will abandon my effort to help you. Your reply also serves as a stark reminder that trying to help anyone is usually a severe waste of time.
     
  28. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
  29. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    First of all, I'm working with 2D physics with the purpose of making a let's say "stereotypical" 2D platformer, in which you could write your own physics and gravity, or you could use the engine physics2D, but your goal is very unlikely to have realistic physics behavior (This could mean instant acceleration, sudden stop, no friction, 0 drag, modifying gravity to make the arc of the jump feel less floaty, have a knockback that sets the player velocity to a high amount for X seconds (and after X seconds the player stop immediately by setting its RB velocity back to 0), etc.)

    I explained this previously... Let's say I have a "Newtonian jump" so that you don't look down on me. As you know, you apply either velocity of force upwards and let gravity do its job pulling you back down. Let's imagine that I measure the time it takes the character to do a full jump (from the time it takes off the ground, through the peak of the jump, to the time it lands), and let's say it takes him 2 seconds. Now, I want to measure how long it takes to reach the peak of the jump, and let's say it takes him half that amount (1s). Now, whether I tap the jump button so slightly or I keep the button pressed, the character is going to perform a full jump. And there's a code to let your player stop jumping if you ever release the jump button (buttonUP). There're many ways in which this can be done, but let's imagine that you just set the RB Y velocity to 0 so the player starts falling immediately under the force of gravity (Again, many 2d platformers have this feature).
    To sum up:
    - If you hold the jump button the player does a full jump (whose peak is 1s)
    - If you release the button the player will immediately fall down at any moment during the jump time.

    Now, The
    shortJumpTime
    condition is there to have a minimum jump time, that I set to 0.1s (a tenth of the peak of the jump). This of course is optional, but is there in case you don't want your player to perform a ridiculously small jump when spamming the jump button.

    I hope this clarifies it.
     
  30. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Wrong. You can help people while being polite or rude. Either way, it's going to be helpful or useful. All I'm saying is that it's unnecessary to have that attitude.

    And also, you're coming with that mindset here (from your previous experiences) and I felt it the very first moment you started being condescending.

    Kurt-Dekker on the other hand, while he can be harsh sometimes, is trying to help genuinely, not to show off his knowledge or to remind you that "you're WRONG, and you shouldn't be doing whatever you're doing because you don't have a perfect understanding, unlike me". (Obviously, this is just an exaggeration, no hard feelings)
     
    Last edited: Jul 30, 2022
  31. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    Let's try to wrap this thread up on a successful note for everybody.

    I have made a simple jump-key-hold-time to jump-height demo.

    It uses an AnimationCurve to map the hold time to jump height.

    It is set up for 0.05s hold time (and below) to map to a height of 2, and 0.12s hold time (and above) to map to a height of 6.

    I threw it in my ProximityButtons project, where I have a growing bunch of other random controllers.

    See code for limitations and simplifications, as well as ways to improve upon it.

    See the
    DemoVariableJumpHeight
    scene. This is the script driving it:

    https://github.com/kurtdekker/proxi.../DemoVariableJumpHeight/VariableJumpHeight.cs

    proximity_buttons is presently hosted at these locations:

    https://bitbucket.org/kurtdekker/proximity_buttons

    https://github.com/kurtdekker/proximity_buttons

    https://gitlab.com/kurtdekker/proximity_buttons

    https://sourceforge.net/projects/proximity-buttons/
     
    GuirieSanchez likes this.
  32. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    Oh the hilarity... apparently back in December 2021, in that same project I already made one of these... so now there are two!!!!

    In addition to the
    DemoVariableJumpHeight
    scene noted above, there is also
    DemoHoldForHigherJump


    They use completely different ways of configuring. The DemoHoldForHigherJump can do either time based or else frame based timings.
     
  33. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Thank you for the demos, there's a lot of helpful info!
    Btw, which version of unity do you use?

    I've been testing the
    DemoVariableJumpHeight
    and I put a timer to the jump so it stops at 0.5s (using Time.deltaTime to measure it). I used a
    Debug.Log
    to capture the exact time registered when it stops.

    I also placed a trigger BoxCollider2D on top that represents lava and says "touching lava" if the character ever touches it. I placed this lava trigger at a height that will be reached by this 0.5s jump by the skin of one’s teeth:

    upload_2022-7-30_23-56-58.png

    TEST:
    Case 1) When full jumping at +800fps, the character always touches the lava, with no exception. Also, the Debug.Log shows a really close number to the one setup, which is 0.5f.
    Case 2) When full jumping at 60fps (v-sync on), the character sometimes touches the lava, sometimes not, and oftentimes does not reach the lava by a big margin.

    I know we've discussed this already, but the conclusion I can draw from it is that I cannot (or should not) rely on
    Time.deltaTime
    for this kind of timer where it needs to be accurate even at lower framerates. Is there an alternative? I don't know
     
  34. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    I think you'll find that Time.deltaTime just doesn't enter (at least not directly in scripting) into the picture.

    Here's why:

    - when you hold forever, the final
    workTimer
    might vary based on framerate.

    - BUT! That value is never used: the
    workTime
    is put into
    guardedTime
    , which would clamp at the upper bounds
    MaxJumpHold


    So for all long-hold max jumps regardless of frame rate,
    guardedTime
    will be
    MaxJumpHold


    After that there's no more frame timing considerations: compute the height, check intent, one time only compute the velocity and set it...

    I just tested some more:

    Go into TimeManager, lock that inspector window.

    Now run the DemoVariableJumpHeight

    Try fixed time step of 0.1, then 0.01, then 0.001

    You'll find the jumping is close within a frame always.
     
    GuirieSanchez likes this.
  35. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Yep, this is in fact very clever. This works as a preparation for the jump so that you can clamp/assign the values and do the whole setup before the jump starts, so the jump will perform the same across framerates; unfortunately, it can't work on the fly (the character is jumping already and you need to do certain actions based on the jump time).

    And, as you said:
    I found already that I cannot rely on
    Time.deltaTime
    for this task since the delta range becomes greater and greater the lower the framerates get.
     
  36. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Little update:

    Despite the fact that I was recommended alternatives to accomplish the stopJump (meeting the 2 conditions I described: (1) buttonPressUP and (2) some custom short time has passed) that don't make use of a timer, I was offered no a single suggestion (again, to accomplish that exact behavior and not others). After much testing though, I finally found an alternative to stop the jump on the 2 conditions I described that does NOT make use of a timer or
    Time.deltaTime
    for that matter. This "solution" is consistent across framerates and it lets me customize the jump as much as I want (for instance, changing gravity values in mid-jump as many times as I'd like). If anyone is interested just let me know.

    However, even if I accomplished one of the things for which I planned to use an accurate and frame-independent timer, I cannot use an accurate timer for many other situations. For example, I wanted to implement accurate timers for input buffers. The input buffers I'm using are making use of
    Time.deltaTime
    , which work accurately only for higher framerates. If your game is running at 50-60fps you can expect a relatively considerable margin of error.
    I believe the answer could have been stated from the beginning: no, there isn't an accurate timer since they're frame-dependent (at least that I'm aware of). Or perhaps better said, there's no accurate timer across framerates (especially in low framerates) when it comes to using
    Time.deltaTime
    as a timer (not using it for physics).
     
    Last edited: Jul 31, 2022
  37. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    It wasn't my intention to be rude, I just have very little time to commit to anyone atm (sadly), and I'm merely highlighting how you could save time for both others and yourself. This will surely lead you to a better experience on the forum as people will be able to help you more readily. I'm aware you're not in a mindset to acknowledge how exactly this is helpful to you in the long run and I'm sorry if you don't appreciate my comments as helpful. This is exactly why I was bitter because it seems I have wasted time trying to help in a way that came across as antagonism, which is not the case. I'm definitely not here to show off, but the decision is yours to make, and maybe we just don't parry all too well.
     
    GuirieSanchez likes this.
  38. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    Now please, with your final conclusion in mind, try to reread my posts from the beginning, without assuming any kind of attitude. I am not a native speaker, perhaps there is some cultural barrier that prevents you from subsuming my overarching message:

    1) deltaTime and physics don't mix very well.

    2) don't modify the environment of the physics simulation expecting it to work as intended, without properly understanding the whole of the physics simulation, which is honestly near-impossible, because there is also Unity with its quirks standing in the way of such understanding. in this regard, we're all more akin to bus drivers than bus engineers.

    3) try to frame your question in a way that doesn't funnel other people in what you expect to be the solution, rather be open to radically new approaches by not expecting anything, i.e. deltaTime was largely not a problem to begin with, but the feature you wanted to reliably make happen is systematically convoluted in such a way where deltaTime appears to be part of a bigger puzzle.

    4) please please please, do not just jump to a conclusion that everything harsh that comes your way, comes from a position of condescendence, ignorance or disdain, or that it's personal. we are not anyone's mother, we have everyday stresses and, more often than not, little to no time to be gratuitously and benevolently generous 100%. I don't earn money by doing this, and I'm too old to show off. I have offered a decent amount of help in this forum, sometimes successfully sometimes not, but people, from my perspective, are sometimes leeches; consider both angles and only then we can have a fruitful conversation.

    that said, I've said no to your question in my first post. if you follow my answer closely, this is irrelevant to deltaTime, but has to do with how you react to any notion of change: via Update. therefore, no matter what you do, you react to all state changes in a frame-dependent manner.

    you cannot escape such a reality even philosophically, however, you might want to build a separate time piece (through monitoring system oscillators) that works on an event system basis (independently and asynchronously). you still have to update your visuals in a frame-dependent manner, but there is a lot else you could do in pure C# (albeit with much more work and with dubious results, mostly because you still have to interconnect your logic with the frame-dependent mechanisms sooner than later).

    to fundamentally override such behavior, would mean to modify the actual underlying engine in C++, however it is proprietary so this is not legally possible (perhaps you can pay for a license that grants you such powers idk). on another note, I don't really believe there is a single, ordinary, low to medium budget project (especially indie), where any such modifications are necessary, and so the answer to your original question is honestly to stop being so purist about your approach in general. I think that's a good advice to receive in general. please understand there is no attitude behind this, just wisdom -> "take what you have and make the most out of it" should be a motto of any practical Unity developer. otherwise, don't make compromises and write your own engine, which is probably the same lesson taken upside down!

    there are a lot of things in Unity that aren't and won't be perfect, but in my long practice, this is to be expected from any sufficiently large piece of software, and the best expectation when approaching such systems is to expect pareto principle in how difficult they are to make peace with. yes development will be a breeze 80% of the time, mostly with the things and features that one (naively) presupposes to be the most difficult. brace for the remaining 20% though, or you will surely pull your hair out.

    finally, with all this put to rest, the proper title of this thread should've been "I'm having a difficult time trying to make my rigidbody jump independently of framerate" as this would open up a more conscious conversation and engage people with more proactive and hands-on experience with what you're trying to achieve. that's all I wanted to say really, no need to get angry about such an advice.
     
    Kurt-Dekker likes this.
  39. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    Wow, you write far better than half of my fellow countrymen!

    Props to you and your country's educational system!!!
     
  40. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    @orionsyndrome Thank you for the long and very helpful analysis of the thread. I agree with you on many things you have stated in your post, and I really appreciate that you took your time and conveyed your perspective in an educated, calm, and mature manner. However, and I do not want to piss you off, I would like to disclose the few things I disagree with down below. I also don't want to come across as unappreciative of the support brought by you guys: first and foremost, whether we had some misunderstandings or not, I really appreciate the help and the exceptionally well-explained functionality of the system regarding this issue. I also think it's not just useful for me, but also it's useful for future wanderers seeking an answer to this topic. As a disclaimer, I want to say that the following is just an opinion from my perspective and I could very well be wrong, so take it with a pinch of salt.

    - First of all, that day I re-read the thread multiple times because I wanted to make sure to grasp all the content and useful information while running the tests so as to bring some results back. Your first comment was very well written, clear, and instructive: (1) a thorough explanation of fixed steps, FixedUpdate, and their bound to physics. (2) the dependency of
    Time.deltaTime
    and
    Time.fixedDeltaTime
    to framerate because the granularity of the Delta goes up the lower the framerate (even fixed steps are bound to some extent to framerate in the end).
    (3) Here is the confusing part for me of your first post IMO (not saying it's on you). Let me start by quoting what you just said in your last post:
    Despite the fact that you just said that your reply was a no to my question about "having a reliable and frame-independent timer", you actually explained the following in the 4th paragraph of your first post (making things frame-independent):
    Now, one could argue that this part could be a little misleading or confusing. I don't doubt there's a way to make a reliant "t" value, but definitely using
    Time.deltaTime
    will not be the way, as you just mentioned in your very last post (which again, was a no to my question about "having a reliable and frame-independent timer").
    Notwithstanding, the following assertion indicated to me that this "t" value (if implemented correctly) would be indeed reliable and frame-independent:
    And this was exactly what I was aiming for: slows down, speeds up, stutters...

    "I can simply accumulate Time.deltaTime each passing frame", this, of course, made me think I was doing something wrong since I was doing exactly that (accumulating
    Time.deltaTime
    ) and experiencing for the first time the increments of the Delta the lower the framerates got. I know you are aware of this, but I want you to understand that this might be confusing for a person not as advanced in programming as you are.
    Finally, to get a real
    Time.deltaTime
    that is not useless, you indicated how to put it into practice (by taking the example of multiplying the desired velocity with
    Time.deltaTime
    .

    Now, if you read my answer to your first post again, you will realize that I acknowledged your very useful post correctly and draw the correct conclusions:
    (1)
    Time.deltaTime
    is frame-dependent so I can't use it as a frame-independent timer (and I still don't know whether alternatives to it exist)
    (2) a working example of your explanations about using
    Time.deltaTime
    for a fixed value when calculating v/s, by "gamedevplanet".
    (3) and then I showed a quick capture of how I was implementing the timer so far, and therefore experiencing inconsistencies in accuracy.

    So, at that point, I was close to the final conclusion: According to you, I should not rely on
    Time.deltaTime
    , which is totally correct. Do you honestly think, however, this is a complete answer to the question of "reliable and frame-independent timer"? Think about it, I'm getting a negative answer about the implementation using
    Time.deltaTime
    that I showcased,
    I was using
    Time.deltaTime
    because when you google or spend some time doing research in Unity forums, the most common answer is to use a
    Time.deltaTime
    . And of course, most answers state that
    Time.deltaTime
    is not perfect and can have inconsistencies (exactly as you said and further explain in your 2nd post.
    but I'm left with no clue of whether this is still possible by using other alternatives. One would expect the following answers:
    (1) Your implementation is not ok + There's no way of getting an accurate/reliable and frame-independent timer.
    (2) Your implementation is not ok + However, there is a way of getting an accurate/reliable and frame-independent timer. I don't want/can't/have time (you name it) to tell you, so you do research for yourself (or wait and see if someone coming here could help you out).
    (3) Your implementation is not ok + However, there is a way of getting an accurate/reliable and frame-independent timer. I recommend you check (whatever source).
    (4) Your implementation is not ok + However, there is a way of getting an accurate/reliable and frame-independent timer. Here's an example of how you'd do it.

    To summarize it, your post was correct and arguably a little confusing for people not that advance in programming; but again, this is my fault, not yours. In any case, you could expect people to be confused or at least eager to keep asking and investigating more about it. And, because the answer was not complete (as I showed above), I couldn't consider the thread over, so I left it open for more contributions, which I think it's understandable.

    I won't go into the details of the subsequent posts, but I think it's evident that misunderstandings started to arise when Kurt-Dekker joined (and this was totally my fault)
    Kurt was rightfully saying that
    Time.deltaTime
    was reliable and frame-independent, and I was considering his assertion through the lens of total accuracy, especially in lower framerates, therefore the misunderstanding.

    [HERE IS THE IMPORTANT PART: I'm sorry for the long post, but the next part is where I think the hard feelings proliferated. I will explain it from my perspective not to demonstrate or to say I'm right (I hardly think I could be), but rather to let you know why I considered some of the answers condescending]

    Now, IMO, things started to go wrong when the focus of the thread switched to the jump itself (which I simply showcased as a single implementation for which I planned to use a reliable and frame-independent (super-accurate/whatever) timer). Here is an attitude or rather approach that I saw multiple times when reading Unity's forums: people will answer from their own perspective (which is totally normal and understandable) and sometimes will assume that the objective (or even standards) of the person asking should be that of the person answering. This could be extrapolated for the jump case here: Kurt's opinion and perspective about the jump was rigid (examples):
    Your opinion on this is rigid too:
    Of course, I accept we can have different opinions on the jump itself, so I remained cool, calm, and collected. In fact, I tried to modify the jump implementation to a Newtonian one for the sake of recovering track/focus to the timer and not the jump. My only issue with these posts (about the jump) is that you guys led me to think that the inconsistencies I was experiencing were due to me tweaking the jump and the physics involved (modifying gravity, etc.).
    And, if you were wondering, here is what I particularly considered condescending:
    Again, from your perspective, this could be a regular and acceptable comment. But take in mind that you don't know anything about the person you're talking to, so some assumptions you may make could be wrong.
    Before I started my project (2D platformer Metroidvania-like), I did a thorough investigation of the multiple games that fall into this category. I analyzed many of them and I tried to do research on the techniques, game structure, engineering, and other aspects employed in their development process. Most 2D games that fall into this category employ this technique where you tweak gravity values during the jump to avoid the floaty feeling. This has even been done back in the 80s by games like Super Mario Bros. You may argue yes, but they didn't use the in-built physics system, they wrote their own. Well, a recent example is Hollow Knight (made by Team Cherry using Unity) in which they use the in-built RigidBody2D component and the physics system (you can have a look at their Twitter account's post here). They crafted the jump by tweaking the gravity values during the jump so as to have a floaty-feeling and rapid ascend till the apex of the jump, then the gravity value is recovered so that you feel at the apex that you're fighting against a force pushing against you (gravity ofc), and then fall back down even faster with an increased gravity value. I'm not saying this technique is right or wrong, I'm also not demoting by any means Newtonian physics or accurate physics simulations. All I'm saying is that this is a very well-used and known technique that is suitable for games of this kind. I'm asking that you be more comprehensive with people, especially when not having a complete understanding of their purpose, knowledge, methods used, etc.

    Going back to the example of Hollow Knight, if you try the game you'll notice that the jump works accurately across framerates, and consistently (I haven't seen reviews of people complaining about it, nor that I encountered it myself when enjoying the game). This made me think it's doable: I just had to find a way to do it correctly (and fortunately I found it).

    Finally, and I think this was a little rude of you (but I don't have hard feelings), is assuming (and even sending that link about the X and Y) that my purpose (or title) in the thread is (or even should be) something from your own perspective. I highly disagree here. Yes, I wanted to accomplish the jump (and I put it as an example because I thought a timer could have been useful), but YES, I have to accomplish a thousand things more besides the jump. My main focus was and still is on the accurate timer, not on the thousand things I have to accomplish.
    I don't know if you read my last post in which I commented on something for which I wanted to use an accurate timer:
    Not that is really important though; at very lower frame rates you would have bigger problems than a buffered input not being read. But again, even for the sake of learning, I think my question is and should be related to the timer.

    I hope my explanation clarifies a little bit my main focus and why I chose the title you see in the thread. In all, even if after all this we still disagree, I don't think it's a big deal. I still appreciate the many things I learned from you and from Kurt-Dekker, for which I'm really grateful! Also thanks for reading.
     
    Last edited: Aug 4, 2022
  41. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    @Kurt-Dekker Thank you so much for your kind words, but unfortunately, I owe nothing to my country's educational "system". You could say I am well learned in spite of it, not thanks to it. I grew up in a country torn by three wars, two of which were civil, harsh economic crises, embargoes, run by criminal elites and foreign puppets, rendered useless through artificial cultural and intellectual isolation. Serbia is a beautiful country but one with a very turbulent history and troubled geopolitics. Things went up and down since forever, I had my "big" moments and breakthroughs, but right now I can't have a passport, I'm 42 and I don't know how I will pay the rent this month. I used to be someone but nowadays I can't get proper work any more, except as a corporate slave (I can't handle it), a delivery boy, or somewhere in agriculture, and my wife is slowly fading away from constantly making the ends meet. Boy I wish showing off on a forum could compensate for that. Your words trigger an avalanche of emotions in me. But thank you nevertheless.

    @GuirieSanchez One thing caught my eye in your post. I think your issue lies in the fact that you're assuming that physics simulation is responsible for the general motion in games, whereas that's not the case. You see, in a typical game dev, we actually do not simulate physics, we just cherry pick some of its features in the Unity's API and implement a so-called faux physics, which is more of an approximation than true physics.

    Hollow Knight does not use physics, just like Mario, Celeste, and Dead Cells. The only resemblance of physics these games use is perhaps in the way they account for collisions, because that's something everybody needs, and it can become a very persistent cause of bugs if not tackled fundamentally. Especially in a platformer. Instead, they simply emulate gravity and introduce motion that resembles forces we're accustomed to. If you're making a responsive platformer, you definitely do not want to use rigidbodies for motion, but only for collision detection, and I think this is where you got confused.

    This is where I think you misread my original post. At very low framerates your issue is having such low framerates in the first place. The simple timer (obtained through accumulating deltaTime) is accurate enough, however you can't react to it properly because of such long interruptions, simply because you're still reacting in a frame-dependent manner, as you cannot interrupt Unity's rendering pipeline in the middle of a frame and be like "oh this just came in".

    If we're talking only spikes, then you shouldn't worry too much about something that's an edge-case, if not, than you should really worry about having low framerates in the first place. You need to design your game around the desired target framerate, which is historically a multiple of 30 i.e. 30, 60, 90 or 120, and every deviation should be treated as exceptional and only so far as to remedy the perceived instability of the underlying simulation that might break your animations, collisions or general feel you wish to convey. In multiplayer there are other considerations to be had, but if your only concern is a responsive single-player, try to learn from a demo how people introduce faux physics in their games before you try to haphazardly combine Unity's physics with any sort of engaging gameplay.

    Once again, Unity's physics is computed independently of the graphics update loop, and has its own tick rate, which is LOWER than the refresh rate. It is not well-suited for responsive platformers, it is also computationally slow, if applied to wrong things it can look janky and clumsy, and in general it is needlessly complex for what you want to achieve.