From 7119622cca4420a2e2dc08de4b45252b0bcb9db8 Mon Sep 17 00:00:00 2001 From: skyace65 Date: Sun, 11 Aug 2024 19:25:07 -0400 Subject: [PATCH] Add physics interpolation docs to 4.3 --- tutorials/physics/index.rst | 1 + .../2d_and_3d_physics_interpolation.rst | 60 +++++ .../advanced_physics_interpolation.rst | 37 +++ .../img/fti_graph_fixed_ticks.webp | Bin 0 -> 2696 bytes .../img/fti_graph_interpolated.webp | Bin 0 -> 9856 bytes .../img/physics_interpolation_mode.webp | Bin 0 -> 6476 bytes tutorials/physics/interpolation/index.rst | 14 + .../physics_interpolation_introduction.rst | 244 ++++++++++++++++++ ...hysics_interpolation_quick_start_guide.rst | 18 ++ .../using_physics_interpolation.rst | 159 ++++++++++++ 10 files changed, 533 insertions(+) create mode 100644 tutorials/physics/interpolation/2d_and_3d_physics_interpolation.rst create mode 100644 tutorials/physics/interpolation/advanced_physics_interpolation.rst create mode 100644 tutorials/physics/interpolation/img/fti_graph_fixed_ticks.webp create mode 100644 tutorials/physics/interpolation/img/fti_graph_interpolated.webp create mode 100644 tutorials/physics/interpolation/img/physics_interpolation_mode.webp create mode 100644 tutorials/physics/interpolation/index.rst create mode 100644 tutorials/physics/interpolation/physics_interpolation_introduction.rst create mode 100644 tutorials/physics/interpolation/physics_interpolation_quick_start_guide.rst create mode 100644 tutorials/physics/interpolation/using_physics_interpolation.rst diff --git a/tutorials/physics/index.rst b/tutorials/physics/index.rst index d220491164b..56d8c8433be 100644 --- a/tutorials/physics/index.rst +++ b/tutorials/physics/index.rst @@ -19,3 +19,4 @@ Physics collision_shapes_3d large_world_coordinates troubleshooting_physics_issues + interpolation/index diff --git a/tutorials/physics/interpolation/2d_and_3d_physics_interpolation.rst b/tutorials/physics/interpolation/2d_and_3d_physics_interpolation.rst new file mode 100644 index 00000000000..d501625f68a --- /dev/null +++ b/tutorials/physics/interpolation/2d_and_3d_physics_interpolation.rst @@ -0,0 +1,60 @@ +.. _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. + +.. note:: currently only 2D physics interpolation works in Godot. + 3D interpolation is expected to come in a future update. + +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 *off*. +- 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 + behaviour of 2D nodes therefore requires a little more thought and planning. +- On the positive side, pivot behaviour in the scene tree is perfectly + preserved during interpolation in 2D, which gives super smooth behaviour. + +reset_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 ``CPUParticles2D`` and ``Particles2D`` are not supported for physics +interpolation in 2D. diff --git a/tutorials/physics/interpolation/advanced_physics_interpolation.rst b/tutorials/physics/interpolation/advanced_physics_interpolation.rst new file mode 100644 index 00000000000..24c2b8a2068 --- /dev/null +++ b/tutorials/physics/interpolation/advanced_physics_interpolation.rst @@ -0,0 +1,37 @@ +.. _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. + +.. note:: currently only 2D physics interpolation works in Godot. + 3D interpolation is expected to come in a future update. + +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` (or branch of the :ref:`SceneTree`), +and have the finer control of performing interpolation manually. + +This is possible using the :ref:`Node.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. + +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. diff --git a/tutorials/physics/interpolation/img/fti_graph_fixed_ticks.webp b/tutorials/physics/interpolation/img/fti_graph_fixed_ticks.webp new file mode 100644 index 0000000000000000000000000000000000000000..70d668aaf27881e658c0cbca00b30a0de8dd5863 GIT binary patch literal 2696 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 WL?_pa4BTHAFPh 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 literal 0 HcmV?d00001 diff --git a/tutorials/physics/interpolation/img/physics_interpolation_mode.webp b/tutorials/physics/interpolation/img/physics_interpolation_mode.webp new file mode 100644 index 0000000000000000000000000000000000000000..e2e31e646006ee5930cc9a1ac21a5a3ba5bf828d GIT binary patch literal 6476 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 literal 0 HcmV?d00001 diff --git a/tutorials/physics/interpolation/index.rst b/tutorials/physics/interpolation/index.rst new file mode 100644 index 00000000000..92ce6c24484 --- /dev/null +++ b/tutorials/physics/interpolation/index.rst @@ -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 diff --git a/tutorials/physics/interpolation/physics_interpolation_introduction.rst b/tutorials/physics/interpolation/physics_interpolation_introduction.rst new file mode 100644 index 00000000000..05ccb389335 --- /dev/null +++ b/tutorials/physics/interpolation/physics_interpolation_introduction.rst @@ -0,0 +1,244 @@ +.. _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:`ProjectSettings.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 dealing with the +problem. It is supported by Godot 3.5 and later in 3D (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. + +.. note:: currently only 2D physics interpolation works in Godot. + 3D interpolation is expected to come in a future update. + +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:: python + + 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`. + +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:: python + + x_interpolated = x_prev + ((x_curr - x_prev) * 0.2) + +So substituting our ``x_prev`` as 10, and ``x_curr`` as 30: + +.. code-block:: python + + 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. diff --git a/tutorials/physics/interpolation/physics_interpolation_quick_start_guide.rst b/tutorials/physics/interpolation/physics_interpolation_quick_start_guide.rst new file mode 100644 index 00000000000..1407523aa92 --- /dev/null +++ b/tutorials/physics/interpolation/physics_interpolation_quick_start_guide.rst @@ -0,0 +1,18 @@ +.. _doc_physics_interpolation_quick_start_guide: + +Quick start guide +================= + +.. note:: currently only 2D physics interpolation works in Godot. + 3D interpolation is expected to come in a future update. + +- Turn on physics interpolation: :ref:`ProjectSettings.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` + on nodes *after* you first position or teleport them, to prevent + "streaking" +- Temporarily try setting :ref:`ProjectSettings.physics/common/physics_ticks_per_second` + to 10 to see the difference with and without interpolation. diff --git a/tutorials/physics/interpolation/using_physics_interpolation.rst b/tutorials/physics/interpolation/using_physics_interpolation.rst new file mode 100644 index 00000000000..cbdcf95755b --- /dev/null +++ b/tutorials/physics/interpolation/using_physics_interpolation.rst @@ -0,0 +1,159 @@ +.. _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. + +.. note:: currently only 2D physics interpolation works in Godot. + 3D interpolation is expected to come in a future update. + +Turn on the physics interpolation setting +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The first step is to turn on physics interpolation in :ref:`ProjectSettings.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` + 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`. + 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 behaviour 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` +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`, +or where you should be using your own custom interpolation on a node. +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.