To write the smoothest and most responsive games you need to:
Currently Godot does not meet these requirements where your options are:
Currently I don't see many functions in the API that can help with this and that's the lower level and cumbersome Shape2D.collide or the even lower level Physics2DDirectSpaceState.collide_shape. However, it's burdensome to call these manually on every object that responds to collisions.
There are two solutions to this:
Either of these will negatively impact determinism or outright break it, but in games that only use physics for mainly responding to collisions and overlaps then this is hardly a problem.
i remember seeing a suggestion about changing the physics frame rate to the monitor's refresh rate. if the developer multiplies everything by a delta, they should be fine on all monitor rates, right?
however, is that how it should be done? i mean, the physics loop will be 144 times per second compared to just 60. does it effect collisions and create noticable delay that much to be worth it?
cause i'm on a 144hz monitor, and use move_and_slide
in _process
for player movement.
projectile collisions and stuff all run at the default physics rate of 60/s and it seems to work fine. no delay with collisions at all. from what i've experienced anyway...
Setting the physics FPS to the refresh rate could work, there's just no easy way to do it right now. #21486 makes a good point, if we could get the refresh rate and vsync is enabled, we could just set the physics FPS to match refresh rate. Right now we can do Engine.iterations_per_second = Engine.get_frames_per_second()
but this depends on what the game is doing at the moment. You'd have to do it a few seconds into the title screen, that's the earliest time you can get an accurate reading from get_frames_per_second
. But that's error prone, if the player alt tabs or moves the window or something you'll get a very wrong value.
Using this script I am seeing a very small number of "bad frames" popping up. Occasionally you'll get a delta that's just barely too short (0.15 instead of 0.1666), there are the expected multiple calls on frames with a higher delta, and confusingly there was a negative delta (I'm not even sure how a negative delta can happen). However, the frames with 0 physics frames because the delta is barely too short are the problem, setting the physics FPS to the refresh rate doesn't necessarily guarantee that there will be a physics frame every rendered frame. However, it almost guarantees that overlaps are processed at most one frame later.
So as the engine is today, I think the best way would be to set physics FPS to refresh rate, move in process and just deal with the 1 frame delay. At least then it's almost always 1 frame and rarely more. Or set it to something higher as long as the overhead is not too much.
extends Node
var physics_frames_this_frame = 0
var physics_fps_set = false
func _physics_process(delta):
physics_frames_this_frame += 1
func _process(delta):
if physics_fps_set and physics_frames_this_frame != 1:
print("bad frame " + str(physics_frames_this_frame) + " " + str(delta))
physics_frames_this_frame = 0
func _on_Timer_timeout():
Engine.iterations_per_second = Engine.get_frames_per_second()
print(Engine.get_frames_per_second())
physics_fps_set = true
AFAIK physics processing happens _at least_ once per frame. Correct me if I'm wrong.
The number of physics iterations is based on the amount of time since last frame. If only 1/144th of a second has passed since the last physics iteration and the physics FPS is on 60 then there will be no physics frames that frame.
@timoschwarzer from what i found, each frame for physics is default at 1/60 (0.0167). you can confirm this by using movement code in _physics_process
and set your monitor to 144hz or 75hz, or whatever. there will be extreme jitter (not updating fast enough to correspond to the monitor refresh rate). but once movement code goes in _process
, it's very smooth.
this is why it's very important not to use movement code inside physics_process
. i don't know how many times i've downloaded a game from facebook, itch, etc and the screen tearing is so overbearing it makes me want to stop playing. the dev is basically limiting their game to 60hz monitors. which IMO is not a good idea. even though 60hz monitors are still popular, there are tons of players who use 75hz and above.
these findings are for 2d only, not sure about 3d
I wonder if physics/common/physics_jitter_fix
is just broken/not working. Because it should prevent what you are experiencing.
Fix to improve physics jitter, specially on monitors where refresh rate is different than physics FPS
Same jitter problem with Godot 3.1.1 while running a very simple 2D game prototype with a fast moving rigid body character.
Problem happens due to slow hardware rendering the game at 43-45 fps (varying) while Physics is set to fixed 60Hz. I tried to increase physics rate, which reduces the jitter a bit, but it is still very real.
I tried to change physics_jitter_fix
to 1.0 from the default 0.5, but it did not help either. Is that fix suitable only for the case when rendering fps is higher than physics rate?
Is there a way to force exactly one physics evaluation per frame rendered, using the same delta for both?
@girng
this is why it's very important not to use movement code inside
physics_process
.
I also have my movement code in physics_process
, because it calls apply_impulse
to add a user controlled thrust vector. Moving user input handling into process
does not eliminate the jitter caused by the varying number of physics frames evaluated per rendered frame. Reason is that the character's position is changed by physics due to its velocity.
Only solution seems to be running a fixed number of physics steps per frame rendered. For example if delta < 1.0/60, then run 1 physics frame with the same delta. If delta is higher, then split the delta to be less than 1.0/60 and run multiple steps, so physics can remain precise even if the rendering frame rate temporarily drops.
Is there a way to override the scheduling of physics processing in GDScript, like manually invoking physics processing with specific delta?
Integrated the deltas of both the physics and render process functions:
extends RigidBody2D
var physics_time := 0.0
var render_time := 0.0
func _physics_process(delta):
physics_time += delta
add_torque((PI - angular_velocity) * 50)
func _process(delta):
render_time += delta
var time_diff := render_time - physics_time
print("time difference %8.3fms" % (time_diff * 1000))
The script is on a RigidBody with a Line2D visual, the added torque force spins it up and stabilizies at a near constant rotation speed. The test runs at 60fps with VSync ON on a 60Hz display. Physics rate is left at the default 60Hz.
Screen recordings:
Notice when no physics step is happening between two rendered frames. That appears as jitter/judder when the rod stops momentarily, then catches up (appears to jump back and forth).
When the line is rotating smoothly the printout shows a constant difference between the physics and render timestamps:
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
time difference 11.451ms
Game FPS: 60
When the rotating line has a visible jitter, then the timestamps slip and physics jumps over frame rendering, causing the visible artifact:
time difference 12.947ms
time difference 12.947ms
time difference 12.947ms
time difference 12.947ms
time difference 12.947ms
time difference 12.947ms
time difference 12.947ms
time difference 12.947ms
time difference 12.947ms
time difference 12.947ms
time difference 12.947ms
time difference 12.947ms
time difference 0.000ms
time difference 1.389ms
time difference 2.778ms
time difference 4.167ms
time difference 5.556ms
time difference 6.944ms
time difference 7.750ms
time difference 7.750ms
time difference 7.750ms
time difference 7.884ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.273ms
time difference 9.376ms
time difference 10.271ms
time difference 10.271ms
time difference 10.271ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 11.312ms
time difference 12.701ms
time difference 12.701ms
time difference 12.701ms
time difference 12.701ms
time difference 12.701ms
time difference 12.701ms
time difference 12.701ms
Game FPS: 59
Currently it is impossible to make smooth physics based movement even with both the rendering and physics rates fixed and having a very basic (fast to render) scene.
I suggest refactoring the Main::iteration
method in main/main.cpp
, so it can be maintained.
In order to fix the visible jitter/judder issue physics needs to be invoked one or more times with appropriate deltas before rendering of each frame. Physics timestamps needs to catch up exactly to the rendering timestamp in order to avoid jitter.
Doing so would guarantee that there is no drift between physics and rendering regardless of the current rendering frame rate or intermittent slowdowns due to other concurrent processing, OS background tasks, CPU throttling, etc.
# On starting scene
physics_time := 0.0
render_time := 0.0
# Before calling _process for each frame rendered
physics_period := 1.0 / physics_fps
while physics_time < render_time:
drift := render_time - physics_time
delta := min(physics_period, drift)
invoke_physics_processing(delta)
physics_time += delta
# After rendering frame
render_time += render_delta
# (it could just assing current monotonic timestamps, e.g. wall clock time)
I'm actually currently working on fixing this. See my PR #30226 and these issues:
https://www.gamedev.net/blogs/entry/2265460-fixing-your-timestep-and-evaluating-godot/
There can be multiple sources of jitter which act on top of each other, which can make identifying 'one cause' of jitter difficult. Some sources:
In this issue you are mainly talking about (1). My personal preferred method of dealing with this (and is used in many AAA games now) is fixing your timestep. This involves using the physics / movement at a fixed tick rate (as is done now within Godot). However in order for this to work you also need to perform interpolation at render time. This is already possible to an extent from gdscript, however PR #30226 will allow this to be done more accurately.
To save users the bother of writing interpolation themselves, I have also written a smoothing node:
https://www.youtube.com/watch?v=SFLwCR2KEJ8
However I am currently seeing whether it can be integrated within the spatial node so any derived nodes can use this functionality with minimal user intervention.
As well as fixed timestep interpolation, the two methods in this thread are common alternatives:
(1) was probably the first method used to try to deal with varying frame durations. It kind of works, but physics and gameplay can give wildly different results when you drive it at varying deltas.
(2) is a simple way of trying to combine the benefits of fixed timesteps and accurate positions during frames which don't match up. You step the timestep at regular intervals, then add a 'mini step' to make up the difference to get to the exact frame time.
And then there is fixed timestep with interpolation. This is slightly more complex to implement, but provides the best visual quality with completely stable physics / gameplay, which is why it is in many cases now the de facto choice.
There are other trade offs too. Consider that the timing of the timesteps can also affect things like input. So some would argue the use of semi-fixed over fixed in the case of fast twitch games.
Another thing to bear in mind is multiplayer, where you might have one server and several clients. Here fixed timestep makes total sense, with the server running fixed timestep and the clients interpolating. Usually multiplayer interpolation has to be more advanced because it has to take account of things like lost packets, variation in timing of packet arrival etc.
Anyway hopefully that should help.. As I say I am currently working to get fixed timestep working. I can also have a look afterwards and see if it is possible to switch stepping mode for Godot, maybe have a selection between fixed timestep, semi fixed and frame based stepping. As far as the physics stepping in the Godot timing code I've examined so far I'm hoping this will be straightforward, although there may be unforeseen interdependencies (for example if there are multithreading issues).
_BTW, @viktor-ferenczi if you want to try adding semi-fixed go for it, it should (hopefully) be quite simple, however you will need to maintain the old method and have the method switchable in project settings, with fixed timestep as default so as not to break existing games. You may need to examine / replace the jitter fix. Actually I think once these other methods are working, there is a good argument for removing the jitter fix (if it does what I think it does). If not as I say I will get to it soon._
@lawnjelly Thank you for your detailed post and all the hard work. Currently I'm working on a game prototype, which shows severe jitter on my notebook, while works perfectly on stronger desktop PC. That's how I noticed the problem. I'm going to test your branch with the prototype. (Since I've just started to dig into the relevant part of the Godot code base I cannot promise to add semi-fixed quickly.)
Just a heads up on this (and to @viktor-ferenczi ), I've refactored the main_timer_sync to make it a bit more sensible, easier to understand and expandable. I've now added a choice of timestep for physics:
I've also added a rudimentary delta smoothing system to deal with timing variations from the OS. The fixed timestep will work best in conjunction with 2 new smoothing (interpolation) nodes, one for 3d and one for 2d. I was intending to have these available for testing already as a module, but this is waiting on the interpolation fraction PR getting merged.
It will probably take me another week or two to iron out any kinks before submitting the timestep stuff as a PR. It shouldn't break anything existing, just will add the new methods in the godot settings pages, defaulting to the old fixed timestep with jitter fix.
Feature and improvement proposals for the Godot Engine are now being discussed and reviewed in a dedicated Godot Improvement Proposals (GIP) (godotengine/godot-proposals) issue tracker. The GIP tracker has a detailed issue template designed so that proposals include all the relevant information to start a productive discussion and help the community assess the validity of the proposal for the engine.
The main (godotengine/godot) tracker is now solely dedicated to bug reports and Pull Requests, enabling contributors to have a better focus on bug fixing work. Therefore, we are now closing all older feature proposals on the main issue tracker.
Thanks in advance!
Most helpful comment
I'm actually currently working on fixing this. See my PR #30226 and these issues:
30068
10388
https://www.gamedev.net/blogs/entry/2265460-fixing-your-timestep-and-evaluating-godot/
There can be multiple sources of jitter which act on top of each other, which can make identifying 'one cause' of jitter difficult. Some sources:
In this issue you are mainly talking about (1). My personal preferred method of dealing with this (and is used in many AAA games now) is fixing your timestep. This involves using the physics / movement at a fixed tick rate (as is done now within Godot). However in order for this to work you also need to perform interpolation at render time. This is already possible to an extent from gdscript, however PR #30226 will allow this to be done more accurately.
To save users the bother of writing interpolation themselves, I have also written a smoothing node:
https://www.youtube.com/watch?v=SFLwCR2KEJ8
However I am currently seeing whether it can be integrated within the spatial node so any derived nodes can use this functionality with minimal user intervention.
As well as fixed timestep interpolation, the two methods in this thread are common alternatives:
(1) was probably the first method used to try to deal with varying frame durations. It kind of works, but physics and gameplay can give wildly different results when you drive it at varying deltas.
(2) is a simple way of trying to combine the benefits of fixed timesteps and accurate positions during frames which don't match up. You step the timestep at regular intervals, then add a 'mini step' to make up the difference to get to the exact frame time.
And then there is fixed timestep with interpolation. This is slightly more complex to implement, but provides the best visual quality with completely stable physics / gameplay, which is why it is in many cases now the de facto choice.
There are other trade offs too. Consider that the timing of the timesteps can also affect things like input. So some would argue the use of semi-fixed over fixed in the case of fast twitch games.
Another thing to bear in mind is multiplayer, where you might have one server and several clients. Here fixed timestep makes total sense, with the server running fixed timestep and the clients interpolating. Usually multiplayer interpolation has to be more advanced because it has to take account of things like lost packets, variation in timing of packet arrival etc.
Anyway hopefully that should help.. As I say I am currently working to get fixed timestep working. I can also have a look afterwards and see if it is possible to switch stepping mode for Godot, maybe have a selection between fixed timestep, semi fixed and frame based stepping. As far as the physics stepping in the Godot timing code I've examined so far I'm hoping this will be straightforward, although there may be unforeseen interdependencies (for example if there are multithreading issues).
_BTW, @viktor-ferenczi if you want to try adding semi-fixed go for it, it should (hopefully) be quite simple, however you will need to maintain the old method and have the method switchable in project settings, with fixed timestep as default so as not to break existing games. You may need to examine / replace the jitter fix. Actually I think once these other methods are working, there is a good argument for removing the jitter fix (if it does what I think it does). If not as I say I will get to it soon._