|
| 1 | +.. _doc_advanced_physics_interpolation: |
| 2 | + |
| 3 | +Advanced physics interpolation |
| 4 | +============================== |
| 5 | + |
| 6 | +Although the previous instructions will give satisfactory results in a lot of games, |
| 7 | +in some cases you will want to go a stage further to get the best possible results |
| 8 | +and the smoothest possible experience. |
| 9 | + |
| 10 | +Exceptions to automatic physics interpolation |
| 11 | +--------------------------------------------- |
| 12 | + |
| 13 | +Even with physics interpolation active, there may be some local situations where |
| 14 | +you would benefit from disabling automatic interpolation for a |
| 15 | +:ref:`Node<class_Node>` (or branch of the :ref:`SceneTree<class_SceneTree>`), and |
| 16 | +have the finer control of performing interpolation manually. |
| 17 | + |
| 18 | +This is possible using the :ref:`Node.physics_interpolation_mode<class_Node_property_physics_interpolation_mode>` |
| 19 | +property which is present in all Nodes. If you for example, turn off interpolation |
| 20 | +for a Node, the children will recursively also be affected (as they default to |
| 21 | +inheriting the parent setting). This means you can easily disable interpolation for |
| 22 | +an entire subscene. |
| 23 | + |
| 24 | +The most common situation where you may want to perform your own interpolation is |
| 25 | +Cameras. |
| 26 | + |
| 27 | +Cameras |
| 28 | +~~~~~~~ |
| 29 | + |
| 30 | +In many cases, a :ref:`Camera3D<class_Camera3D>` can use automatic interpolation |
| 31 | +just like any other node. However, for best results, especially at low physics tick |
| 32 | +rates, it is recommended that you take a manual approach to camera interpolation. |
| 33 | + |
| 34 | +This is because viewers are very sensitive to camera movement. For instance, a |
| 35 | +Camera3D that realigns slightly every 1/10th of a second (at 10tps tick rate) will |
| 36 | +often be noticeable. You can get a much smoother result by moving the camera each |
| 37 | +frame in ``_process``, and following an interpolated target manually. |
| 38 | + |
| 39 | +Manual camera interpolation |
| 40 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 41 | + |
| 42 | +Ensure the camera is using global coordinate space |
| 43 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 44 | + |
| 45 | +The very first step when performing manual camera interpolation is to make sure the |
| 46 | +Camera3D transform is specified in *global space* rather than inheriting the |
| 47 | +transform of a moving parent. This is because feedback can occur between the |
| 48 | +movement of a parent node of a Camera3D and the movement of the camera Node itself, |
| 49 | +which can mess up the interpolation. |
| 50 | + |
| 51 | +There are two ways of doing this: |
| 52 | + |
| 53 | +1) Move the Camera3D so it is independent on its own branch, rather than being a child of a moving object. |
| 54 | + |
| 55 | +.. image:: img/fti_camera_worldspace.webp |
| 56 | + |
| 57 | +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. |
| 58 | + |
| 59 | +Typical example |
| 60 | +^^^^^^^^^^^^^^^ |
| 61 | + |
| 62 | +A typical example of a custom approach is to use the ``look_at`` function in the |
| 63 | +Camera3D every frame in ``_process()`` to look at a target node (such as the player). |
| 64 | + |
| 65 | +But there is a problem. If we use the traditional ``get_global_transform()`` on a |
| 66 | +Camera3D "target" node, this transform will only focus the Camera3D on the target *at |
| 67 | +the current physics tick*. This is *not* what we want, as the camera will jump |
| 68 | +about on each physics tick as the target moves. Even though the camera may be |
| 69 | +updated each frame, this does not help give smooth motion if the *target* is only |
| 70 | +changing each physics tick. |
| 71 | + |
| 72 | +get_global_transform_interpolated() |
| 73 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 74 | + |
| 75 | +What we really want to focus the camera on, is not the position of the target on |
| 76 | +the physics tick, but the *interpolated* position, i.e. the position at which the |
| 77 | +target will be rendered. |
| 78 | + |
| 79 | +We can do this using the :ref:`Spatial.get_global_transform_interpolated<class_Node3D_method_get_global_transform_interpolated>` |
| 80 | +function. This acts exactly like getting :ref:`Spatial.global_transform<class_Node3D_property_global_transform>` |
| 81 | +but it gives you the *interpolated* transform (during a ``_process()`` call). |
| 82 | + |
| 83 | +.. important:: ``get_global_transform_interpolated()`` should only be used once or |
| 84 | + twice for special cases such as cameras. It should **not** be used |
| 85 | + all over the place in your code (both for performance reasons, and |
| 86 | + to give correct gameplay). |
| 87 | + |
| 88 | +.. note:: Aside from exceptions like the camera, in most cases, your game logic |
| 89 | + should be in ``_physics_process()``. In game logic you should be calling |
| 90 | + ``get_global_transform()`` or ``get_transform()``, which will give the |
| 91 | + current physics transform (in global or local space respectively), which |
| 92 | + is usually what you will want for gameplay code. |
| 93 | + |
| 94 | +Example manual camera script |
| 95 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 96 | + |
| 97 | +Here is an example of a simple fixed camera which follows an interpolated target: |
| 98 | + |
| 99 | +.. code-block:: gdscript |
| 100 | +
|
| 101 | + extends Camera3D |
| 102 | + |
| 103 | + # Node that the camera will follow |
| 104 | + var _target |
| 105 | + |
| 106 | + # We will smoothly lerp to follow the target |
| 107 | + # rather than follow exactly |
| 108 | + var _target_pos : Vector3 = Vector3() |
| 109 | + |
| 110 | + func _ready() -> void: |
| 111 | + # Find the target node |
| 112 | + _target = get_node("../Player") |
| 113 | + |
| 114 | + # Turn off automatic physics interpolation for the Camera3D, |
| 115 | + # we will be doing this manually |
| 116 | + set_physics_interpolation_mode(Node.PHYSICS_INTERPOLATION_MODE_OFF) |
| 117 | + |
| 118 | + func _process(delta: float) -> void: |
| 119 | + # Find the current interpolated transform of the target |
| 120 | + var tr : Transform = _target.get_global_transform_interpolated() |
| 121 | + |
| 122 | + # Provide some delayed smoothed lerping towards the target position |
| 123 | + _target_pos = lerp(_target_pos, tr.origin, min(delta, 1.0)) |
| 124 | + |
| 125 | + # Fixed camera position, but it will follow the target |
| 126 | + look_at(_target_pos, Vector3(0, 1, 0)) |
| 127 | +
|
| 128 | +Mouse look |
| 129 | +^^^^^^^^^^ |
| 130 | + |
| 131 | +Mouse look is a very common way of controlling cameras. But there is a problem. |
| 132 | +Unlike keyboard input which can be sampled periodically on the physics tick, mouse |
| 133 | +move events can come in continuously. The camera will be expected to react and |
| 134 | +follow these mouse movements on the next frame, rather than waiting until the next |
| 135 | +physics tick. |
| 136 | + |
| 137 | +In this situation, it can be better to disable physics interpolation for the camera |
| 138 | +node (using :ref:`Node.physics_interpolation_mode<class_Node_property_physics_interpolation_mode>`) |
| 139 | +and directly apply the mouse input to the camera rotation, rather than apply it in |
| 140 | +``_physics_process``. |
| 141 | + |
| 142 | +Sometimes, especially with cameras, you will want to use a combination of |
| 143 | +interpolation and non-interpolation: |
| 144 | + |
| 145 | +* 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. |
| 146 | +* 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. |
| 147 | + |
| 148 | +There are many permutations and variations of camera types, but it should be clear |
| 149 | +that in many cases, disabling automatic physics interpolation and handling this |
| 150 | +yourself can give a better result. |
| 151 | + |
| 152 | +Disabling interpolation on other nodes |
| 153 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 154 | + |
| 155 | +Although cameras are the most common example, there are a number of cases when you |
| 156 | +may wish other nodes to control their own interpolation, or be non-interpolated. |
| 157 | +Consider for example, a player in a top view game whose rotation is controlled by |
| 158 | +mouse look. Disabling physics rotation allows the player rotation to match the |
| 159 | +mouse in real-time. |
| 160 | + |
| 161 | + |
| 162 | +MultiMeshes |
| 163 | +~~~~~~~~~~~ |
| 164 | + |
| 165 | +Although most visual Nodes follow the single Node single visual instance paradigm, |
| 166 | +MultiMeshes can control several instances from the same Node. Therefore, they have |
| 167 | +some extra functions for controlling interpolation functionality on a |
| 168 | +*per-instance* basis. You should explore these functions if you are using |
| 169 | +interpolated MultiMeshes. |
| 170 | + |
| 171 | +- :ref:`MultiMesh.reset_instance_physics_interpolation<class_MultiMesh_method_reset_instance_physics_interpolation>` |
| 172 | +- :ref:`MultiMesh.set_buffer_interpolated<class_MultiMesh_method_set_buffer_interpolated>` |
| 173 | + |
| 174 | +Full details are in the :ref:`MultiMesh<class_MultiMesh>` documentation. |
0 commit comments