- Home /
Gravity & Framerate
Hello,
i have raycast ballistics system and i cast ray every frame from next position and add gravity to it. But my function for adding gravity does not work properly.
When i use low frame rate, the poing where projectile falls on the ground is closer than with high frame rate. Now i have this, but i need almost same point where projectile falls on the ground.
I need something like this (but idk how to do that :( , i spent lot of time how should correct function look like, but without result):
Please help me, how i should make it correct.
My function for adding gravity:
private Vector3 SetGravityDir(Vector3 dir)
{
Vector3 dirWGravity = dir.normalized * speed + (Physics.gravity * drop * Time.deltaTime);
return dirWGravity * Time.deltaTime;
}
Thank you in advance for the answers.
Answer by Bunny83 · Nov 18, 2017 at 02:20 PM
Any accelerated movement (which is not a linear movement but a quadratic) can't simply be scaled linearly. DeltaTime works fine for any linear process. So for example a constant acceleration applied to a velocity will yield the correct velocity values after "t" time reliably. Applying a constant velocity to a position will yield the correct position after "t" time reliably. However you apply a linearly growing velocity to the position which will not yield the correct values. In general lower framerates will result in a quicker grows.
That's the reason why Unity's physics runs at a constant framerate (FixedUpdate). Have a look at this article. Unity's FixedUpdate roughly does what is described in th "Free the physics" section. I said roughly because Unity doesn't take the min between the set fixedDeltaTime and the current deltaTime but simply always uses the fixedDeltaTime. Unity however does not really implement what is described in "The final touch"
What exactly do you pass to your SetGravityDir method? Also where and how do you actually use the result?
I know, that one way how to fix it is by using FixedFrameRate, so it is only one possible method how to fix it ? I used FidexUpdate and Time.fixedDeltaTime and everything was O$$anonymous$$.
This method sets my dirVector which will be used in castingRay. i give lastDirVector to this method, then i use speed (which makes my vector longer/shorter so "faster in one frame") and result of it is my dirVector for RayCast.
So there isn't any mathematical equation how to fix it in normal Update() ?
Well there are other ways if you do the whole movement equation yourself. However the way you "process" your direction does not represent an actual projectile trajectory. Your vector will always have the length of "speed" (and a tiny bit from the gravity component but that's almost nothing due to dt²). So you only use gravity to slowly rotate your direction downward. Gravity however appies an acceleration to the velocity. So over time your velocity should get larger (if you shoot in an upward angle it would first becomes slower until it reaches the highest point). So you should preserve the actual velocity between each "step".
A common way of fixing a linearly accelerated movement is to apply the velocity in two steps ins$$anonymous$$d of one. Usually the straight forward implementation looks like this:
vel += acc * Time.deltaTime;
pos += vel * Time.deltaTime;
This will lead to exactly the problem you see with different deltaTime values. Just look at extreme situations where dt is simply "1" so 1 frame takes 1 second. If you have a constant acceleration of 10m/s² and just imagine you start with 0 velocity when you look at uniform acceleration formulas you will notice that after 1 second the velocity should be 10 m/s (which it is when using our formula) however the position should be "5" however we get a result of "10". This error becomes smaller and smaller the smaller the "dt" gets.
The solution is to apply half of the old velocity before your apply the acceleration and half of the new velocity after you applied your acceleration. So it becomes something like this:
pos += 0.5f * vel * Time.deltaTime;
vel += acc * Time.deltaTime;
pos += 0.5f * vel * Time.deltaTime;
This will yield much better position values. Here's an example of varying deltaTime:
Time | vel | pos | dt |oldPos + oldVel + newVel
//-----------------------------------------------
0 | 0 | 0 | - |
1 | 10 | 5 | 1 | 0 + 0 + 5 --> 5
1.5 | 15 | 11.25 | 0.5 | 5 + 2.5 + 3.75 --> 11.25
2 | 20 | 20 | 0.5 | 11.25 + 3.75 + 5 --> 20
2.5 | 25 | 31.25 | 0.5 | 20 + 5 + 6.25 --> 31.25
3 | 30 | 45 | 0.5 | 31.25 + 6.25 + 7.5 --> 45
4 | 40 | 80 | 1 | 45 + 15 + 20 --> 80
According to s(t) = s0 + v0 * t + 0.5*a*t²
after 4 seconds you should get a position of "80" which we do even the delta time changes between frames.
Thank you for your great answer.
I thought that solution is a little easier. Could you help me little bit more ? (I never knew much about physics.)
pos += 0.5f * vel * Time.deltaTime;
vel += acc * Time.deltaTime;
pos += 0.5f * vel * Time.deltaTime;
What are pos and vel ?
pos is position with datatype Vector3 ? vel is float number or Vector3 too? and what is acc (acceleration?) and where i get it?
I think, that i understood how you calculated this:
Time | vel | pos | dt |oldPos + oldVel + newVel
//-----------------------------------------------
0 | 0 | 0 | - |
1 | 10 | 5 | 1 | 0 + 0 + 5 --> 5
1.5 | 15 | 11.25 | 0.5 | 5 + 2.5 + 3.75 --> 11.25
2 | 20 | 20 | 0.5 | 11.25 + 3.75 + 5 --> 20
2.5 | 25 | 31.25 | 0.5 | 20 + 5 + 6.25 --> 31.25
3 | 30 | 45 | 0.5 | 31.25 + 6.25 + 7.5 --> 45
4 | 40 | 80 | 1 | 45 + 15 + 20 --> 80
But idk where or how i can add Gravity vector?
If we imagine, that i have:
V3 pos;
float vel;
??? acc;
//and make function do something like this.
addG(){
pos += 0.5f * vel * Time.deltaTime;
vel += acc * Time.deltaTime;
pos += 0.5f * vel * Time.deltaTime;
}
So where i add that Gravity vector and why pos wont be so "far" like vel?
Thank you alot for your help.
Answer by ColtPtrHun · Mar 26, 2020 at 02:56 PM
@Bunny83 I had the same problem, and the trapezoidal integrator solved it :) Thank you very much! I knew that the rectangular integrator won't be that punctial but it was the least suspecious part at least for me. -Peter