Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
2
Question by Khena_B · Jul 12, 2018 at 11:40 PM · transformframeratetranslatedeltatimeindependent

Jump not framerate independent

Hey,

I'm using a custom 2D controller, it has a Move() method that uses transform.Translate(velocity) to move the character.

In my player controller script I apply gravity by doing this every frame right before I call Move() :

 velocity.y += gravity * Time.deltaTime; 

My Jump() method is called before the gravity, and sets the velocity.y to the jumpForce once.

At 60+ fps i'm able to jump over a specific amount of tiles, at 30 fps I miss the last tile by a small amount of height, the weird thing is that if I slow down the game with time scale, I reach the same height.

What is going on?

EDIT: Bunny83's solution worked perfectly, I'm going to keep Eno-Khaon's answer because it clearly explains the issue, thanks guys much appreciated

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

3 Replies

· Add your reply
  • Sort: 
avatar image
4
Best Answer

Answer by Eno-Khaon · Jul 13, 2018 at 03:13 AM

Physics calculated on a per-frame basis will ALWAYS* be framerate-dependent. This is why Unity uses FixedUpdate() to alleviate that somewhat by providing a consistent rate of performance to offer reproducible results.

*Well, okay, not always, but the calculations required to make it framerate-independent are non-trivial to the point where it's simpler to wind up close enough instead.

If you calculate where something should be in reality (based on predictive physics calculations), you're assuming that the rate of acceleration will change an infinite number of times per second. In your case (based on iterative physics calculations), however, the acceleration changes at a rate based on the current framerate.

To give an example of this, here's a breakdown of behavior under the following conditions:

1) Gravity accelerates objects at a rate of 1 unit per second.
2) The object "jumps" with a starting upward force of 1 unit per second velocity

 Column A: Current Y-axis speed
 Column B: Pending height change, based on current frame velocity
 Column C: New height after current frame
 
 Example 1: 5 frames per second
 deltaTime per frame: 0.2
 
 |A      |B      |C
 +-------+-------+-------
 |1.0    |0.2    |0.2
 |0.8    |0.16   |0.36
 |0.6    |0.12   |0.48
 |0.4    |0.08   |0.56
 |0.2    |0.04   |0.6

 Final Height: 0.6 at 1 second, 5 frames
 
 -------------------------------
 
 Example 2: 10 frames per second
 deltaTime per frame: 0.1
 
 |A      |B      |C
 +-------+-------+-------
 |1.0    |0.1    |0.1
 |0.9    |0.09   |0.19
 |0.8    |0.08   |0.27
 |0.7    |0.07   |0.34
 |0.6    |0.06   |0.4
 |0.5    |0.05   |0.45
 |0.4    |0.04   |0.49
 |0.3    |0.03   |0.52
 |0.2    |0.02   |0.54
 |0.1    |0.01   |0.55
 
 Final Height: 0.55 at 1 second, 10 frames

 -------------------------------

 A "correct" Final Height would have been 0.5


As you can see, differences in framerate WILL affect the outcome because the framerate directly influences exactly how long the object is moving at any given speed. While the changes in velocity based on acceleration take effect in a true-to-life manner, the changes in position are based on the current velocity for a given frame, scaled by the time that frame took to process.

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Khena_B · Jul 13, 2018 at 03:30 AM 0
Share

I'm having a hard time understanding what you're saying exactly (I'm exhausted), but i've noticed that at a timescale of 0.5 (twice the amount of frames), the character makes it at 30 fps where it doesn't with a timescale of 1, is this what you mean, and how can this be solved, I want a 100% consistent jump height.

avatar image Eno-Khaon Khena_B · Jul 13, 2018 at 03:53 AM 0
Share

Essentially, the point I'm trying to make is that 100% accurate physics calculations, performed iteratively by the framerate of gameplay, are prohibitively costly to calculate. You should aim for a "close enough" kind of approach because true-to-life physics aren't easy to calculate in every situation.

The two main approaches to compensate for this would basically be to either:

A) Use some form of fixed framerate, whether it's utilizing FixedUpdate() for position information or using Time.captureFramerate to ensure that a precise number of frames will render per second of "game time"
or...
B) Calculate your height purely mathematically based on your initial height and elapsed time. Following in the footsteps of the example provided here, it would look something like:

 float newY = startY + ((jumpVelocityY * elapsedTime) - (0.5f * gravityY * elapsedTime * elapsedTime));
avatar image Khena_B Eno-Khaon · Jul 13, 2018 at 04:01 AM 0
Share

Thank you so much, I'm still unable to understand why the final heights are different but my brain is on fire, I'll give it another try tomorrow, as for consistency I don't $$anonymous$$d 100% but there seems to be too much of an inconsistency in my case, where a player could jump an extra tile at high fps, something I've never noticed in any platformers, I don't want to cap the fps as input lag is present with Vsync, and stutter when manually capping, I'll keep trying to figure it out and take a look at the links you posted, thanks again

Show more comments
avatar image
3

Answer by Bunny83 · Jul 13, 2018 at 03:59 AM

While Eno-Khaon is basically right, there is an easy fix for constant accelerations. The main problem is that deltaTime can only be used to make linear things framerate independent. Since a constant acceleration applies a linear change to the velocity, the velocity correctly changes over time. However the position change is not linear but quadratic and it can't be scaled linearly.


The trick is to apply the velocity not once but twice each frame. Once before you do the velocity change and once after the velocity change. You just apply half the velocity each time. This will not make it perfect as we deal with time discrete values while in reality physics happens continuously. Though it will make the errors even out and not drift over time.

 pos += 0.5f * velocity * Time.deltaTime;
 if (jump)
     velocity.y = jumpForce;
 velocity += gravity * Time.deltaTime;
 pos += 0.5f * velocity * Time.deltaTime;


Note that this does only work for constant accelerations / linear velocity changes. So if something like drag is used it won't work since drag is non linear

Comment
Add comment · Show 7 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Jul 13, 2018 at 04:05 AM 0
Share

I've explained that already more in detail in the comments below my answer over here

avatar image Khena_B · Jul 13, 2018 at 06:04 AM 0
Share

This worked perfectly, I'm going to keep my previously accepted answer but mention it in my OP, thank you so much!

avatar image Bip901 · Jul 16, 2018 at 06:52 PM 0
Share

This solution is not working for me... Help please! Here's a simplified version of my script: ("gravity" is a negative number, "movement" is the variable you named "velocity", I'm using Unity's Character Controller)

     private void Update()
     {
         myController.$$anonymous$$ove(movement * 0.5f);
         movement.x = movement.z = 0.0f; //I want my player to only accelerate on the Y axis
         if (Input.Get$$anonymous$$ey(controls.forward$$anonymous$$ey))
         {
             walk = true;
         }                  
 
         if (myController.isGrounded)
         {
             movement.y = 0;
             if (Input.Get$$anonymous$$eyDown(controls.jump$$anonymous$$ey))
             {
                 movement.y = jumpForce; //jumpForce is 0.15
             }
         }
 
         if (walk)
             movement += transform.forward * moveSpeed * Time.deltaTime;
 
         movement.y -= gravity * Time.deltaTime;
 
         myController.$$anonymous$$ove(movement * 0.5f);
     }



avatar image Khena_B Bip901 · Jul 16, 2018 at 07:28 PM 0
Share

You need to set the movement before and after you call $$anonymous$$ove, for example in the case of gravity:

 movement.y -= gravity * 0.5f * Time.deltaTime;
  
 myController.$$anonymous$$ove(movement * Time.deltaTime);
 
 movement.y -= gravity * 0.5f * Time.deltaTime;

You don't need to call $$anonymous$$ove() twice

avatar image Bip901 Khena_B · Jul 17, 2018 at 08:55 AM 0
Share

Thanks for the reply, but it still doesn't work... $$anonymous$$y simplified script now:

     private void Update()
     {
         movement.y -= gravity * 0.5f * Time.deltaTime;
         movement.x = movement.z = 0.0f;
         walk = false;
 
         if (Input.Get$$anonymous$$ey(controls.forward$$anonymous$$ey))
         {
             walk = true;
         }
 
         if (myController.isGrounded)
         {
             movement.y = 0;
             if (Input.Get$$anonymous$$eyDown(controls.jump$$anonymous$$ey))
             {
                 movement.y = jumpForce;
             }
         }
 
         if (walk)
                 movement += transform.forward * moveSpeed * Time.deltaTime;
 
         myController.$$anonymous$$ove(movement);
         movement.y -= gravity * 0.5f * Time.deltaTime;
     }
Show more comments
avatar image
0

Answer by KarlFa · Jul 12, 2018 at 11:51 PM

Try using Time.fixedDeltaTime

Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Khena_B · Jul 13, 2018 at 12:12 AM 0
Share

I'm not using FixedUpdate at all, my character doesn't move with Box2D's physics

avatar image imM4TT · Jul 13, 2018 at 12:13 AM 0
Share

Time.deltaTime & Time.fixedDeltaTime works respectivly in their function
If you call Time.fixedDeltaTime in the basic Update function it will dont work and call Time.deltaTime.
You should execute your $$anonymous$$ove function in the fixedUpdate ins$$anonymous$$d of the Update()

avatar image Khena_B imM4TT · Jul 13, 2018 at 12:23 AM 0
Share

This is a raycast based controller, movement and collision detection needs to happen every frame, in Update()

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

115 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Framerate Independent Firing Rate (delta time?) 2 Answers

How do I modify the current transform? 2 Answers

How to make a object translate alonge an other 2 Answers

Can't move instantiated prefab 2 Answers

Camera transform not responding after load 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges