mirror of
https://github.com/godotengine/godot-docs.git
synced 2025-12-31 17:49:03 +03:00
Port physics interpolation docs from 3.6
This commit is contained in:
@@ -19,3 +19,4 @@ Physics
|
||||
collision_shapes_3d
|
||||
large_world_coordinates
|
||||
troubleshooting_physics_issues
|
||||
interpolation/index
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
.. _doc_2d_and_3d_physics_interpolation:
|
||||
|
||||
2D and 3D physics interpolation
|
||||
===============================
|
||||
|
||||
Generally 2D and 3D physics interpolation work in very similar ways. However, there
|
||||
are a few differences, which will be described here.
|
||||
|
||||
Global versus local interpolation
|
||||
---------------------------------
|
||||
|
||||
- In 3D, physics interpolation is performed *independently* on the **global
|
||||
transform** of each 3D instance.
|
||||
- In 2D by contrast, physics interpolation is performed on the **local transform**
|
||||
of each 2D instance.
|
||||
|
||||
This has some implications:
|
||||
|
||||
- In 3D, it is easy to turn interpolation on and off at the level of each ``Node``,
|
||||
via the ``physics_interpolation_mode`` property in the Inspector, which can be
|
||||
set to ``On``, ``Off``, or ``Inherited``.
|
||||
|
||||
.. figure:: img/physics_interpolation_mode.webp
|
||||
:align: center
|
||||
|
||||
- However this means that in 3D, pivots that occur in the ``SceneTree`` (due to
|
||||
parent child relationships) can only be interpolated **approximately** over the
|
||||
physics tick. In most cases this will not matter, but in some situations the
|
||||
interpolation can look slightly wrong.
|
||||
- In 2D, interpolated local transforms are passed down to children during
|
||||
rendering. This means that if a parent is set to ``physics_interpolation_mode``
|
||||
``On``, but the child is set to ``Off``, the child will still be interpolated if
|
||||
the parent is moving. *Only the child's local transform is uninterpolated.*
|
||||
Controlling the on / off behavior of 2D nodes therefore requires a little more
|
||||
thought and planning.
|
||||
- On the positive side, pivot behavior in the scene tree is perfectly preserved
|
||||
during interpolation in 2D, which gives super smooth behaviour.
|
||||
|
||||
Resetting physics interpolation
|
||||
-------------------------------
|
||||
|
||||
Whenever objects are moved to a completely new position, and interpolation is not
|
||||
desired (so as to prevent a "streaking" artefact), it is the responsibility of the
|
||||
user to call ``reset_physics_interpolation()``.
|
||||
|
||||
The good news is that in 2D, this is automatically done for you when nodes first
|
||||
enter the tree. This reduces boiler plate, and reduces the effort required to get
|
||||
an existing project working.
|
||||
|
||||
.. note:: If you move objects *after* adding to the scene tree, you will still need
|
||||
to call ``reset_physics_interpolation()`` as with 3D.
|
||||
|
||||
2D Particles
|
||||
------------
|
||||
|
||||
Currently only ``CPUParticles2D`` are supported for physics interpolation in 2D. It
|
||||
is recommended to use a physics tick rate of at least 20-30 ticks per second to
|
||||
keep particles looking fluid.
|
||||
|
||||
``Particles2D`` (GPU particles) are not yet interpolated, so for now it is
|
||||
recommended to convert to ``CPUParticles2D`` (but keep a backup of your
|
||||
``Particles2D`` in case we get these working).
|
||||
|
||||
Other
|
||||
-----
|
||||
|
||||
- ``get_global_transform_interpolated()`` - this is currently only available for 3D.
|
||||
- ``MultiMeshes`` - these should be supported in both 2D and 3D.
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
.. _doc_advanced_physics_interpolation:
|
||||
|
||||
Advanced physics interpolation
|
||||
==============================
|
||||
|
||||
Although the previous instructions will give satisfactory results in a lot of games,
|
||||
in some cases you will want to go a stage further to get the best possible results
|
||||
and the smoothest possible experience.
|
||||
|
||||
Exceptions to automatic physics interpolation
|
||||
---------------------------------------------
|
||||
|
||||
Even with physics interpolation active, there may be some local situations where
|
||||
you would benefit from disabling automatic interpolation for a
|
||||
:ref:`Node<class_Node>` (or branch of the :ref:`SceneTree<class_SceneTree>`), and
|
||||
have the finer control of performing interpolation manually.
|
||||
|
||||
This is possible using the :ref:`Node.physics_interpolation_mode<class_Node_property_physics_interpolation_mode>`
|
||||
property which is present in all Nodes. If you for example, turn off interpolation
|
||||
for a Node, the children will recursively also be affected (as they default to
|
||||
inheriting the parent setting). This means you can easily disable interpolation for
|
||||
an entire subscene.
|
||||
|
||||
The most common situation where you may want to perform your own interpolation is
|
||||
Cameras.
|
||||
|
||||
Cameras
|
||||
~~~~~~~
|
||||
|
||||
In many cases, a :ref:`Camera3D<class_Camera3D>` can use automatic interpolation
|
||||
just like any other node. However, for best results, especially at low physics tick
|
||||
rates, it is recommended that you take a manual approach to camera interpolation.
|
||||
|
||||
This is because viewers are very sensitive to camera movement. For instance, a
|
||||
Camera3D that realigns slightly every 1/10th of a second (at 10tps tick rate) will
|
||||
often be noticeable. You can get a much smoother result by moving the camera each
|
||||
frame in ``_process``, and following an interpolated target manually.
|
||||
|
||||
Manual camera interpolation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Ensure the camera is using global coordinate space
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The very first step when performing manual camera interpolation is to make sure the
|
||||
Camera3D transform is specified in *global space* rather than inheriting the
|
||||
transform of a moving parent. This is because feedback can occur between the
|
||||
movement of a parent node of a Camera3D and the movement of the camera Node itself,
|
||||
which can mess up the interpolation.
|
||||
|
||||
There are two ways of doing this:
|
||||
|
||||
1) Move the Camera3D so it is independent on its own branch, rather than being a child of a moving object.
|
||||
|
||||
.. image:: img/fti_camera_worldspace.webp
|
||||
|
||||
2) Call :ref:`Node3D.top_level<class_Node3D_property_top_level>` and set this to ``true``, which will make the Camera ignore the transform of its parent.
|
||||
|
||||
Typical example
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
A typical example of a custom approach is to use the ``look_at`` function in the
|
||||
Camera3D every frame in ``_process()`` to look at a target node (such as the player).
|
||||
|
||||
But there is a problem. If we use the traditional ``get_global_transform()`` on a
|
||||
Camera3D "target" node, this transform will only focus the Camera3D on the target *at
|
||||
the current physics tick*. This is *not* what we want, as the camera will jump
|
||||
about on each physics tick as the target moves. Even though the camera may be
|
||||
updated each frame, this does not help give smooth motion if the *target* is only
|
||||
changing each physics tick.
|
||||
|
||||
get_global_transform_interpolated()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
What we really want to focus the camera on, is not the position of the target on
|
||||
the physics tick, but the *interpolated* position, i.e. the position at which the
|
||||
target will be rendered.
|
||||
|
||||
We can do this using the :ref:`Spatial.get_global_transform_interpolated<class_Node3D_method_get_global_transform_interpolated>`
|
||||
function. This acts exactly like getting :ref:`Spatial.global_transform<class_Node3D_property_global_transform>`
|
||||
but it gives you the *interpolated* transform (during a ``_process()`` call).
|
||||
|
||||
.. important:: ``get_global_transform_interpolated()`` should only be used once or
|
||||
twice for special cases such as cameras. It should **not** be used
|
||||
all over the place in your code (both for performance reasons, and
|
||||
to give correct gameplay).
|
||||
|
||||
.. note:: Aside from exceptions like the camera, in most cases, your game logic
|
||||
should be in ``_physics_process()``. In game logic you should be calling
|
||||
``get_global_transform()`` or ``get_transform()``, which will give the
|
||||
current physics transform (in global or local space respectively), which
|
||||
is usually what you will want for gameplay code.
|
||||
|
||||
Example manual camera script
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Here is an example of a simple fixed camera which follows an interpolated target:
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
extends Camera3D
|
||||
|
||||
# Node that the camera will follow
|
||||
var _target
|
||||
|
||||
# We will smoothly lerp to follow the target
|
||||
# rather than follow exactly
|
||||
var _target_pos : Vector3 = Vector3()
|
||||
|
||||
func _ready() -> void:
|
||||
# Find the target node
|
||||
_target = get_node("../Player")
|
||||
|
||||
# Turn off automatic physics interpolation for the Camera3D,
|
||||
# we will be doing this manually
|
||||
set_physics_interpolation_mode(Node.PHYSICS_INTERPOLATION_MODE_OFF)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
# Find the current interpolated transform of the target
|
||||
var tr : Transform = _target.get_global_transform_interpolated()
|
||||
|
||||
# Provide some delayed smoothed lerping towards the target position
|
||||
_target_pos = lerp(_target_pos, tr.origin, min(delta, 1.0))
|
||||
|
||||
# Fixed camera position, but it will follow the target
|
||||
look_at(_target_pos, Vector3(0, 1, 0))
|
||||
|
||||
Mouse look
|
||||
^^^^^^^^^^
|
||||
|
||||
Mouse look is a very common way of controlling cameras. But there is a problem.
|
||||
Unlike keyboard input which can be sampled periodically on the physics tick, mouse
|
||||
move events can come in continuously. The camera will be expected to react and
|
||||
follow these mouse movements on the next frame, rather than waiting until the next
|
||||
physics tick.
|
||||
|
||||
In this situation, it can be better to disable physics interpolation for the camera
|
||||
node (using :ref:`Node.physics_interpolation_mode<class_Node_property_physics_interpolation_mode>`)
|
||||
and directly apply the mouse input to the camera rotation, rather than apply it in
|
||||
``_physics_process``.
|
||||
|
||||
Sometimes, especially with cameras, you will want to use a combination of
|
||||
interpolation and non-interpolation:
|
||||
|
||||
* A first person camera may position the camera at a player location (perhaps using :ref:`Spatial.get_global_transform_interpolated<class_Node3D_method_get_global_transform_interpolated>`), but control the Camera rotation from mouse look *without* interpolation.
|
||||
* A third person camera may similarly determine the look at (target location) of the camera using :ref:`Spatial.get_global_transform_interpolated<class_Node3D_method_get_global_transform_interpolated>`, but position the camera using mouse look *without* interpolation.
|
||||
|
||||
There are many permutations and variations of camera types, but it should be clear
|
||||
that in many cases, disabling automatic physics interpolation and handling this
|
||||
yourself can give a better result.
|
||||
|
||||
Disabling interpolation on other nodes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Although cameras are the most common example, there are a number of cases when you
|
||||
may wish other nodes to control their own interpolation, or be non-interpolated.
|
||||
Consider for example, a player in a top view game whose rotation is controlled by
|
||||
mouse look. Disabling physics rotation allows the player rotation to match the
|
||||
mouse in real-time.
|
||||
|
||||
|
||||
MultiMeshes
|
||||
~~~~~~~~~~~
|
||||
|
||||
Although most visual Nodes follow the single Node single visual instance paradigm,
|
||||
MultiMeshes can control several instances from the same Node. Therefore, they have
|
||||
some extra functions for controlling interpolation functionality on a
|
||||
*per-instance* basis. You should explore these functions if you are using
|
||||
interpolated MultiMeshes.
|
||||
|
||||
- :ref:`MultiMesh.reset_instance_physics_interpolation<class_MultiMesh_method_reset_instance_physics_interpolation>`
|
||||
- :ref:`MultiMesh.set_buffer_interpolated<class_MultiMesh_method_set_buffer_interpolated>`
|
||||
|
||||
Full details are in the :ref:`MultiMesh<class_MultiMesh>` documentation.
|
||||
BIN
tutorials/physics/interpolation/img/fti_camera_worldspace.webp
Normal file
BIN
tutorials/physics/interpolation/img/fti_camera_worldspace.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
BIN
tutorials/physics/interpolation/img/fti_graph_fixed_ticks.webp
Normal file
BIN
tutorials/physics/interpolation/img/fti_graph_fixed_ticks.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
BIN
tutorials/physics/interpolation/img/fti_graph_interpolated.webp
Normal file
BIN
tutorials/physics/interpolation/img/fti_graph_interpolated.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
14
tutorials/physics/interpolation/index.rst
Normal file
14
tutorials/physics/interpolation/index.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
.. _doc_physics_interpolation:
|
||||
|
||||
Physics Interpolation
|
||||
=====================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-physics-interpolation
|
||||
|
||||
physics_interpolation_quick_start_guide
|
||||
physics_interpolation_introduction
|
||||
using_physics_interpolation
|
||||
advanced_physics_interpolation
|
||||
2d_and_3d_physics_interpolation
|
||||
@@ -0,0 +1,232 @@
|
||||
.. _doc_physics_interpolation_introduction:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Physics ticks and rendered frames
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
One key concept to understand in Godot is the distinction between physics ticks
|
||||
(sometimes referred to as iterations or physics frames), and rendered frames. The
|
||||
physics proceeds at a fixed tick rate (set in :ref:`Project Settings > Physics > Common > Physics Tick per Second<class_ProjectSettings_property_physics/common/physics_ticks_per_second>`),
|
||||
which defaults to 60 ticks per second.
|
||||
|
||||
However, the engine does not necessarily **render** at the same rate. Although many
|
||||
monitors refresh at 60 Hz (cycles per second), many refresh at completely different
|
||||
frequencies (e.g. 75 Hz, 144 Hz, 240 Hz or more). Even though a monitor may be able
|
||||
to show a new frame e.g. 60 times a second, there is no guarantee that the CPU and
|
||||
GPU will be able to *supply* frames at this rate. For instance, when running with
|
||||
V-Sync, the computer may be too slow for 60 and only reach the deadlines for 30
|
||||
FPS, in which case the frames you see will change at 30 FPS (resulting in
|
||||
stuttering).
|
||||
|
||||
But there is a problem here. What happens if the physics ticks do not coincide with
|
||||
frames? What happens if the physics tick rate is out of phase with the frame rate?
|
||||
Or worse, what happens if the physics tick rate is *lower* than the rendered frame
|
||||
rate?
|
||||
|
||||
This problem is easier to understand if we consider an extreme scenario. If you set
|
||||
the physics tick rate to 10 ticks per second, in a simple game with a rendered
|
||||
frame rate of 60 FPS. If we plot a graph of the positions of an object against the
|
||||
rendered frames, you can see that the positions will appear to "jump" every 1/10th
|
||||
of a second, rather than giving a smooth motion. When the physics calculates a new
|
||||
position for a new object, it is not rendered in this position for just one frame,
|
||||
but for 6 frames.
|
||||
|
||||
.. image:: img/fti_graph_fixed_ticks.webp
|
||||
|
||||
This jump can be seen in other combinations of tick / frame rate as glitches, or
|
||||
jitter, caused by this staircasing effect due to the discrepancy between physics
|
||||
tick time and rendered frame time.
|
||||
|
||||
What can we do about frames and ticks being out of sync?
|
||||
--------------------------------------------------------
|
||||
|
||||
Lock the tick / frame rate together?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The most obvious solution is to get rid of the problem, by ensuring there is a
|
||||
physics tick that coincides with every frame. This used to be the approach on old
|
||||
consoles and fixed hardware computers. If you know that every player will be using
|
||||
the same hardware, you can ensure it is fast enough to calculate ticks and frames
|
||||
at e.g. 50 FPS, and you will be sure it will work great for everybody.
|
||||
|
||||
However, modern games are often no longer made for fixed hardware. You will often
|
||||
be planning to release on desktop computers, mobiles, and more. All of which have
|
||||
huge variations in performance, as well as different monitor refresh rates. We need
|
||||
to come up with a better way of dealing with the problem.
|
||||
|
||||
Adapt the tick rate?
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Instead of designing the game at a fixed physics tick rate, we could allow the tick
|
||||
rate to scale according to the end users hardware. We could for example use a fixed
|
||||
tick rate that works for that hardware, or even vary the duration of each physics
|
||||
tick to match a particular frame duration.
|
||||
|
||||
This works, but there is a problem. Physics (*and game logic*, which is often also
|
||||
run in the ``_physics_process``) work best and most consistently when run at a
|
||||
**fixed**, predetermined tick rate. If you attempt to run a racing game physics
|
||||
that has been designed for 60 TPS (ticks per second) at e.g. 10 TPS, the physics
|
||||
will behave completely differently. Controls may be less responsive, collisions /
|
||||
trajectories can be completely different. You may test your game thoroughly at 60
|
||||
TPS, then find it breaks on end users machines when it runs at a different tick
|
||||
rate.
|
||||
|
||||
This can make quality assurance difficult with hard to reproduce bugs, especially
|
||||
in AAA games where problems of this sort can be very costly. This can also be
|
||||
problematic for multiplayer games for competitive integrity, as running the game at
|
||||
certain tick rates may be more advantageous than others.
|
||||
|
||||
Lock the tick rate, but use interpolation to smooth frames in between physics ticks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This has become one of the most popular approaches to deal with the problem,
|
||||
although it is optional and disabled by default.
|
||||
|
||||
We have established that the most desirable physics/game logic arrangement for
|
||||
consistency and predictability is a physics tick rate that is fixed at design-time.
|
||||
The problem is the discrepancy between the physics position recorded, and where we
|
||||
"want" a physics object to be shown on a frame to give smooth motion.
|
||||
|
||||
The answer turns out to be simple, but can be a little hard to get your head around
|
||||
at first.
|
||||
|
||||
Instead of keeping track of just the current position of a physics object in the
|
||||
engine, we keep track of *both the current position of the object, and the previous
|
||||
position* on the previous physics tick.
|
||||
|
||||
Why do we need the previous position *(in fact the entire transform, including
|
||||
rotation and scaling)*? By using a little math magic, we can use **interpolation**
|
||||
to calculate what the transform of the object would be between those two points, in
|
||||
our ideal world of smooth continuous movement.
|
||||
|
||||
.. image:: img/fti_graph_interpolated.webp
|
||||
|
||||
Linear interpolation
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The simplest way to achieve this is linear interpolation, or lerping, which you may
|
||||
have used before.
|
||||
|
||||
Let us consider only the position, and a situation where we know that the previous
|
||||
physics tick X coordinate was 10 units, and the current physics tick X coordinate
|
||||
is 30 units.
|
||||
|
||||
.. note:: Although the maths is explained here, you do not have to worry about the
|
||||
details, as this step will be performed for you. Under the hood, Godot
|
||||
may use more complex forms of interpolation, but linear interpolation is
|
||||
the easiest in terms of explanation.
|
||||
|
||||
The physics interpolation fraction
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If our physics ticks are happening 10 times per second (for this example), what
|
||||
happens if our rendered frame takes place at time 0.12 seconds? We can do some math
|
||||
to figure out where the object would be to obtain a smooth motion between the two
|
||||
ticks.
|
||||
|
||||
First of all, we have to calculate how far through the physics tick we want the
|
||||
object to be. If the last physics tick took place at 0.1 seconds, we are 0.02
|
||||
seconds *(0.12 - 0.1)* through a tick that we know will take 0.1 seconds (10 ticks
|
||||
per second). The fraction through the tick is thus:
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
fraction = 0.02 / 0.10
|
||||
fraction = 0.2
|
||||
|
||||
This is called the **physics interpolation fraction**, and is handily calculated
|
||||
for you by Godot. It can be retrieved on any frame by calling :ref:`Engine.get_physics_interpolation_fraction<class_Engine_method_get_physics_interpolation_fraction>`.
|
||||
|
||||
Calculating the interpolated position
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once we have the interpolation fraction, we can insert it into a standard linear
|
||||
interpolation equation. The X coordinate would thus be:
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
x_interpolated = x_prev + ((x_curr - x_prev) * 0.2)
|
||||
|
||||
So substituting our ``x_prev`` as 10, and ``x_curr`` as 30:
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
x_interpolated = 10 + ((30 - 10) * 0.2)
|
||||
x_interpolated = 10 + 4
|
||||
x_interpolated = 14
|
||||
|
||||
Let's break that down:
|
||||
|
||||
- We know the X starts from the coordinate on the previous tick (``x_prev``) which
|
||||
is 10 units.
|
||||
- We know that after the full tick, the difference between the current tick and the
|
||||
previous tick will have been added (``x_curr - x_prev``) (which is 20 units).
|
||||
- The only thing we need to vary is the proportion of this difference we add,
|
||||
according to how far we are through the physics tick.
|
||||
|
||||
.. note:: Although this example interpolates the position, the same thing can be
|
||||
done with the rotation and scale of objects. It is not necessary to know
|
||||
the details as Godot will do all this for you.
|
||||
|
||||
Smoothed transformations between physics ticks?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Putting all this together shows that it should be possible to have a nice smooth
|
||||
estimation of the transform of objects between the current and previous physics
|
||||
tick.
|
||||
|
||||
But wait, you may have noticed something. If we are interpolating between the
|
||||
current and previous ticks, we are not estimating the position of the object *now*,
|
||||
we are estimating the position of the object in the past. To be exact, we are
|
||||
estimating the position of the object *between 1 and 2 ticks* into the past.
|
||||
|
||||
In the past
|
||||
^^^^^^^^^^^
|
||||
|
||||
What does this mean? This scheme does work, but it does mean we are effectively
|
||||
introducing a delay between what we see on the screen, and where the objects
|
||||
*should* be.
|
||||
|
||||
In practice, most people won't notice this delay, or rather, it is typically not
|
||||
*objectionable*. There are already significant delays involved in games, we just
|
||||
don't typically notice them. The most significant effect is there can be a slight
|
||||
delay to input, which can be a factor in fast twitch games. In some of these fast
|
||||
input situations, you may wish to turn off physics interpolation and use a
|
||||
different scheme, or use a high tick rate, which mitigates these delays.
|
||||
|
||||
Why look into the past? Why not predict the future?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There is an alternative to this scheme, which is: instead of interpolating between
|
||||
the previous and current tick, we use maths to *extrapolate* into the future. We
|
||||
try to predict where the object *will be*, rather than show it where it was. This
|
||||
can be done and may be offered as an option in future, but there are some
|
||||
significant downsides:
|
||||
|
||||
- The prediction may not be correct, especially when an object collides with
|
||||
another object during the physics tick.
|
||||
- Where a prediction was incorrect, the object may extrapolate into an "impossible"
|
||||
position, like inside a wall.
|
||||
- Providing the movement speed is slow, these incorrect predictions may not be too
|
||||
much of a problem.
|
||||
- When a prediction was incorrect, the object may have to jump or snap back onto
|
||||
the corrected path. This can be visually jarring.
|
||||
|
||||
Fixed timestep interpolation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In Godot this whole system is referred to as physics interpolation, but you may
|
||||
also hear it referred to as **"fixed timestep interpolation"**, as it is
|
||||
interpolating between objects moved with a fixed timestep (physics ticks per
|
||||
second). In some ways the second term is more accurate, because it can also be used
|
||||
to interpolate objects that are not driven by physics.
|
||||
|
||||
.. tip:: Although physics interpolation is usually a good choice, there are
|
||||
exceptions where you may choose not to use Godot's built-in physics
|
||||
interpolation (or use it in a limited fashion). An example category is
|
||||
internet multiplayer games. Multiplayer games often receive tick or timing
|
||||
based information from other players or a server and these may not
|
||||
coincide with local physics ticks, so a custom interpolation technique can
|
||||
often be a better fit.
|
||||
@@ -0,0 +1,14 @@
|
||||
.. _doc_physics_interpolation_quick_start_guide:
|
||||
|
||||
Quick start guide
|
||||
=================
|
||||
|
||||
- Turn on physics interpolation: :ref:`Project Settings > Physics > Common > Physics Interpolation<class_ProjectSettings_property_physics/common/physics_interpolation>`
|
||||
- Make sure you move objects and run your game logic in ``_physics_process()``
|
||||
rather than ``_process()``. This includes moving objects directly *and
|
||||
indirectly* (by e.g. moving a parent, or using another mechanism to automatically
|
||||
move nodes).
|
||||
- Be sure to call :ref:`Node.reset_physics_interpolation<class_Node_method_reset_physics_interpolation>`
|
||||
on nodes *after* you first position or teleport them, to prevent "streaking".
|
||||
- Temporarily try setting :ref:`Project Settings > Physics > Common > Physics Tick per Second<class_ProjectSettings_property_physics/common/physics_ticks_per_second>`
|
||||
to 10 to see the difference with and without interpolation.
|
||||
154
tutorials/physics/interpolation/using_physics_interpolation.rst
Normal file
154
tutorials/physics/interpolation/using_physics_interpolation.rst
Normal file
@@ -0,0 +1,154 @@
|
||||
.. _doc_using_physics_interpolation:
|
||||
|
||||
Using physics interpolation
|
||||
===========================
|
||||
|
||||
How do we incorporate physics interpolation into a Godot game? Are there any
|
||||
caveats?
|
||||
|
||||
We have tried to make the system as easy to use as possible, and many existing
|
||||
games will work with few changes. That said there are some situations which require
|
||||
special treatment, and these will be described.
|
||||
|
||||
Turn on the physics interpolation setting
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The first step is to turn on physics interpolation in :ref:`ProjectSettings.physics/common/physics_interpolation<class_ProjectSettings_property_physics/common/physics_interpolation>`.
|
||||
You can now run your game.
|
||||
|
||||
It is likely that nothing looks hugely different, particularly if you are running
|
||||
physics at 60 TPS or a multiple of it. However, quite a bit more is happening
|
||||
behind the scenes.
|
||||
|
||||
.. tip::
|
||||
|
||||
To convert an existing game to use interpolation, it is highly recommended that
|
||||
you temporarily set :ref:`ProjectSettings.physics/common/physics_ticks_per_second<class_ProjectSettings_property_physics/common/physics_ticks_per_second>`
|
||||
to a low value such as 10, which will make interpolation problems more obvious.
|
||||
|
||||
Move (almost) all game logic from _process to _physics_process
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The most fundamental requirement for physics interpolation (which you may be doing
|
||||
already) is that you should be moving and performing game logic on your objects
|
||||
within ``_physics_process`` (which runs at a physics tick) rather than ``_process``
|
||||
(which runs on a rendered frame). This means your scripts should typically be doing
|
||||
the bulk of their processing within ``_physics_process``, including responding to
|
||||
input and AI.
|
||||
|
||||
Setting the transform of objects only within physics ticks allows the automatic
|
||||
interpolation to deal with transforms *between* physics ticks, and ensures the game
|
||||
will run the same whatever machine it is run on. As a bonus, this also reduces CPU
|
||||
usage if the game is rendering at high FPS, since AI logic (for example) will no
|
||||
longer run on every rendered frame.
|
||||
|
||||
.. note:: If you attempt to set the transform of interpolated objects *outside* the
|
||||
physics tick, the calculations for the interpolated position will be
|
||||
incorrect, and you will get jitter. This jitter may not be visible on
|
||||
your machine, but it *will* occur for some players. For this reason,
|
||||
setting the transform of interpolated objects should be avoided outside
|
||||
of the physics tick. Godot will attempt to produce warnings in the editor
|
||||
if this case is detected.
|
||||
|
||||
.. tip:: This is only a *soft rule*. There are some occasions where you might want
|
||||
to teleport objects outside of the physics tick (for instance when
|
||||
starting a level, or respawning objects). Still, in general, you should be
|
||||
applying transforms from the physics tick.
|
||||
|
||||
|
||||
Ensure that all indirect movement happens during physics ticks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Consider that in Godot, Nodes can be moved not just directly in your own scripts,
|
||||
but also by automatic methods such as tweening, animation, and navigation. All
|
||||
these methods should also have their timing set to operate on the physics tick
|
||||
rather than each frame ("idle"), **if** you are using them to move objects (*these
|
||||
methods can also be used to control properties that are not interpolated*).
|
||||
|
||||
.. note:: Also consider that nodes can be moved not just by moving themselves, but
|
||||
also by moving parent nodes in the :ref:`SceneTree<class_SceneTree>`. The
|
||||
movement of parents should therefore also only occur during physics ticks.
|
||||
|
||||
Choose a physics tick rate
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When using physics interpolation, the rendering is decoupled from physics, and you
|
||||
can choose any value that makes sense for your game. You are no longer limited to
|
||||
values that are multiples of the user's monitor refresh rate (for stutter-free
|
||||
gameplay if the target FPS is reached).
|
||||
|
||||
As a rough guide:
|
||||
|
||||
.. csv-table::
|
||||
:header: "Low tick rates (10-30)", "Medium tick rates (30-60)", "High tick rates (60+)"
|
||||
:widths: 20, 20, 20
|
||||
|
||||
"Better CPU performance","Good physics behavior in complex scenes","Good with fast physics"
|
||||
"Add some delay to input","Good for first person games","Good for racing games"
|
||||
"Simple physics behaviour"
|
||||
|
||||
.. note:: You can always change the tick rate as you develop, it is as simple as
|
||||
changing the project setting.
|
||||
|
||||
Call ``reset_physics_interpolation()`` when teleporting objects
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Most of the time, interpolation is what you want between two physics ticks.
|
||||
However, there is one situation in which it may *not* be what you want. That is
|
||||
when you are initially placing objects, or moving them to a new location. Here, you
|
||||
don't want a smooth motion between where the object was (e.g. the origin) and the
|
||||
initial position - you want an instantaneous move.
|
||||
|
||||
The solution to this is to call the :ref:`Node.reset_physics_interpolation<class_Node_method_reset_physics_interpolation>`
|
||||
function. What this function does under the hood is set the internally stored
|
||||
*previous transform* of the object to be equal to the *current transform*. This
|
||||
ensures that when interpolating between these two equal transforms, there will be
|
||||
no movement.
|
||||
|
||||
Even if you forget to call this, it will usually not be a problem in most
|
||||
situations (especially at high tick rates). This is something you can easily leave
|
||||
to the polishing phase of your game. The worst that will happen is seeing a
|
||||
streaking motion for a frame or so when you move them - you will know when you need
|
||||
it!
|
||||
|
||||
There are actually two ways to use ``reset_physics_interpolation()``:
|
||||
|
||||
*Standing start (e.g. player)*
|
||||
|
||||
1) Set the initial transform
|
||||
2) Call ``reset_physics_interpolation()``
|
||||
|
||||
The previous and current transforms will be identical, resulting in no initial
|
||||
movement.
|
||||
|
||||
*Moving start (e.g. bullet)*
|
||||
|
||||
1) Set the initial transform
|
||||
2) Call ``reset_physics_interpolation()``
|
||||
3) Immediately set the transform expected after the first tick of motion
|
||||
|
||||
The previous transform will be the starting position, and the current transform
|
||||
will act as though a tick of simulation has already taken place. This will
|
||||
immediately start moving the object, instead of having a tick delay standing still.
|
||||
|
||||
.. important:: Make sure you set the transform and call
|
||||
``reset_physics_interpolation()`` in the correct order as shown
|
||||
above, otherwise you will see unwanted "streaking".
|
||||
|
||||
Testing and debugging tips
|
||||
--------------------------
|
||||
|
||||
Even if you intend to run physics at 60 TPS, in order to thoroughly test your
|
||||
interpolation and get the smoothest gameplay, it is highly recommended to
|
||||
temporarily set the physics tick rate to a low value such as 10 TPS.
|
||||
|
||||
The gameplay may not work perfectly, but it should enable you to more easily see
|
||||
cases where you should be calling :ref:`Node.reset_physics_interpolation<class_Node_method_reset_physics_interpolation>`,
|
||||
or where you should be using your own custom interpolation on e.g. a
|
||||
:ref:`Camera3D<class_Camera3D>`. Once you have these cases fixed, you can set the
|
||||
physics tick rate back to the desired setting.
|
||||
|
||||
The other great advantage to testing at a low tick rate is you can often notice
|
||||
other game systems that are synchronized to the physics tick and creating glitches
|
||||
which you may want to work around. Typical examples include setting animation blend
|
||||
values, which you may decide to set in ``_process()`` and interpolate manually.
|
||||
Reference in New Issue
Block a user