-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability to iterate physics from script or lock physics to the rendered frame rate #24769
Comments
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 |
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 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.
|
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 this is why it's very important not to use movement code inside these findings are for 2d only, not sure about 3d |
I wonder if
|
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 Is there a way to force exactly one physics evaluation per frame rendered, using the same delta for both? |
I also have my movement code in 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:
When the rotating line has a visible jitter, then the timestamps slip and physics jumps over frame rendering, causing the visible artifact:
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 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.
|
I'm actually currently working on fixing this. See my PR #30226 and these issues: 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: 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. |
Fixes godotengine#24769 Fixes godotengine#24334 The main change in this PR is adding the option in project settings->physics to choose between the old fixed timestep and a new path for semi-fixed timestep. With semi-fixed timestep users can either choose a high physics fps and get the benefit of matching between physics and frame times, or low physics fps and have physics effectively driven by frame deltas. There is also a minor refactor to the main::iteration function, notably moving the physics tick into a separate function, as well as a major refactor to main_timer_sync, separating the common components of timing (timescaling, limiting max physics ticks) from the details of the timestep functionality themselves, which are separated into 2 classes, MainTimerSync_JitterFix (the old fixed timestep) and MainTimerSync_SemiFixed. There is also a modification to allow the existing global time_scale to change the speed of the game without affecting the physics tick rate (i.e. giving consistent physics at different timescales).
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! |
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.
The text was updated successfully, but these errors were encountered: