[WIP] Fixed Timestep Interpolation (3D) (3.x) #52771
Closed
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Adds fixed timestep interpolation to the visual server.
Switchable on and off with project setting.
Loosely based around godotengine/godot-proposals#2753
Notes
no_draw
function is historical and may not be needed, it will probably get taken out.More Info
Physics objects automatically call
set_global_transform_interpolated
, which calls the interpolated functions in the visual server (these fallback to normal funcs if interpolation is switched off in settings). For user code, users should callset_transform_interpolated
,set_global_transform_interpolated
during the physics tick. This sets a propagated 'interpolated' flag in the node and children.Implicit
set_transform
problemA complication with the proposal has turned out to be that
Spatial
contains a large number of functions that internally callset_transform
. This will break interpolation if it currently active, as it will cause a teleport. We've been evaluating various methods to get around this problem.The huge potential for future bugs due to this problem (both in the engine and in user games) leads me to believe we should take the simpler alternative approach provided in #52846, where instead of providing a new API, we simply add an
physics_interpolated
property to each node. Then we add ateleport
function for the rare case when we want to break interpolation for a single tick. This is how fixed timestep interpolation is normally implemented - on by default, with a special case forteleport
s. This has the advantage of being a tried and tested approach, and it means in most cases games can be converted to and from fixed timestep interpolation simply by switching the feature off and on.In this PR I've been trying various approaches suggested by reduz to try and solve this inherent problem in the proposal, but all of them have been problematic in some way in practice, leading to time spent debugging gdscript calls, which I fear may make this too difficult to use for users, or put them off using it.
The obvious extension to the explicit approach in the proposal is to change the entire set of functions available on
Spatial
which implicitly callset_transform
, and add aninterpolated
argument to each:In addition this does not look possible for
set_translation
->set_scale
because these are accessors for the Transform properties and this is incompatible with the property panel.Imo this is needlessly pushing the complexity onto the user, there is no good reason for a user to have to take care in each of these functions when at the end of the day the only thing being set is a single bool per node.
Adding this bool parameter to each function also means in practice searching through game code to find each instance of these functions and rewriting them, for no apparent reason.
Compromise
As a compromise here I've added an internal function
_set_transform_auto
, which is used internally in these above functions, and callsset_translate
orset_translate_interpolated
based on the current flag of the node, thereby shielding the user from this complexity.This means that effectively:
set_transform()
is a combination ofset_interpolated(false);
andset_transform()
set_transform_interpolated()
is a combination ofset_interpolated(true)
andset_transform()
This does mean that if the user isn't intending to call either of these functions (e.g. in a Camera where they use a
look_at
function) they should remember to call eitherset_transform
orset_transform_interpolated
at startup to set theinterpolated
flag of the node. But again this seems more difficult to explain to a user who is scratching their head than to just say 'just set the interpolated property'.Essentially the pros/cons of the two approaches are as follows:
Explicit calls
set_transform_interpolated
when adding new game functionality - we will constantly get changes causing game breaking regressions.Per node setting
teleport
to parts of the game where objects are moved long distances. (Note that this can be done automatically for objects entering the scene tree, so it is quite a rare occurrence in practice.)is_editor_hint
is set, as most editor operations don't want / require interpolation.teleport
call. This should be rare, and if we miss one, the worst regression will be an object being interpolated for a frame when it shouldn't, which isn't that visually jarring, rather than game breaking bugs.Other game changes necessary to get fixed timestep working
Typically for most games some small adjustment for the Camera is all that is required. The easiest is to process the camera in
_physics_process
and call theset_transform_interpolated
orset_global_transform_interpolated
funcs, or just inherit the position from an interpolated parent.If processing a camera in
_process
you will currently need to do the target interpolation yourself, by recording the target previous and current positions etc. I had a little look into automating this, but on discussion with reduz we decided this might be best to leave to the user to start with.e.g. This is 3d platformer demo running at 20 ticks per second with interpolation:
https://user-images.githubusercontent.com/21999379/133752437-4a749906-b574-429c-ac6f-b6f2ea4c21cd.mp4