-
-
Notifications
You must be signed in to change notification settings - Fork 184
Tick Smoothing: Per-tick fixed offsets (pos/rot/scale) + target-based registry #971
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
base: main
Are you sure you want to change the base?
Conversation
tick_smoothers_fix
tick_smoothers_fix
I checked out the code and I think this could be done more easily, but I want to make sure the situation fits first. I assume this happens when your object (eg)evelvator has very fast smoothing, such as over 1 or 2 ticks, while the player object has adaptive smoothing, or more set smoothing larger than the (eg)elevator value? |
Hello! The elevator does not have interpolation. The player's adaptive interpolation is turned off because it causes even more jitter during normal movement. In addition to the player's elevator, any other objects on the map can potentially push the player. Not all of these objects may have the same interpolation, and they may not necessarily have it. In situations where interpolation is difficult to add or may not be applicable, instead of trying to precisely match the interpolation between different objects (and all of them, so that there is no visual intersection of different objects), we can specify an instantaneous offset for graphicalObject for some objects without interpolation. In my case, this allows the KinematicCharacterSystem, which handles any collisions, external forces, or clipping to moving platforms, to ensure that a single method call can immediately remove the lag between the player's graphicalObject and changes to its real position and external objects that could affect it. If necessary, if we have interpolation set up (for example, for an elevator), we can add not the entire fixedOffset, but only the part that would be missing after interpolation to reach the final position. |
Okay, I thought something which may be easier to implement both internally
and in user code is simply changing the interpolation value of the player
at runtime. I think, this might already be possible.
If you set the interpolation to something low, 0, 1, 2 for example, I'd
think you'd get the results you wanted as you intended.
Then when done, just set it back. If this is a viable option and it's not
already possible I think we can get it in officially.
…On Sun, Sep 28, 2025, 5:15 PM waterb ***@***.***> wrote:
*belplaton* left a comment (FirstGearGames/FishNet#971)
<#971 (comment)>
@FirstGearGames <https://github.com/FirstGearGames>
Hello! The elevator does not have anti-aliasing, since this object always
moves other objects, which can cause visual imperfections.
The player's adaptive anti-aliasing is turned off because it causes even
more jitter during normal movement.
In addition to the player's elevator, any other objects on the map can
potentially push the player.
Not all of these objects may have the same interpolation, and they may not
necessarily have it.
In situations where interpolation is difficult to add or may not be
applicable, instead of trying to precisely match the interpolation between
different objects (and all of them, so that there is no visual intersection
of different objects), we can specify an instantaneous offset for
graphicalObject for some objects without interpolation.
In my case, this allows the KinematicCharacterSystem, which handles any
collisions, external forces, or clipping to moving platforms, to ensure
that a single method call can immediately remove the lag between the
player's graphicalObject and changes to its real position and external
objects that could affect it.
If necessary, if we have interpolation set up (for example, for an
elevator), we can add not the entire fixedOffset, but only the part that
would be missing after interpolation to reach the final position.
—
Reply to this email directly, view it on GitHub
<#971 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AGPJC3WB3N5FI2XIWZYSFLL3VBFWRAVCNFSM6AAAAACHNXVPX2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTGNBUGI3DCOJWGE>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
When setting identical interpolation between the player, the elevator, and all other objects that I dragged into the elevator, a problem appeared where, despite the coordination between the player and the elevator, other objects (with NetworkTransform) began to pass through the elevator's View. FixedOffset is useful when we are absolutely sure that one object should move relative to another object by no more and no less than X distance. And if used correctly, taking into account the interpolation of the object that produces the external movement, you can leave the interpolation on the elevator, the player, and other objects, simply passing fixedOffset * currentInterpolatedRatioToAllOffset In addition, I would say that changing the interpolation at runtime is unreliable, because then the player will have a different expirience of the gameplay on the moving platform and outside of it, sometimes worse, sometimes better. |
It is simple a utility that is not required to use, but can be helpful when used correctly. |
The NetworkTransform isn't to be used on the same objects our other smoothers are handling. I'm still thinking what I suggested would work perfectly but I definitely want to make sure I'm understanding the situation fully. The root object is sitting on the platform correctly, right? Even if you are not smoothing the platform at all an interpolation of 1 on our smoothers shouldn't show any desync and will clean up movement. |
Earlier, I created a Feature Request where I describe the problem in detail: #970
Why
Interframe smoothing can’t account for external motion (elevators, conveyors, cutscenes, parent moves). As a result, the graphical child can lag behind its target/root, causing visible separation and jitter (as in the screenshot).
This PR lets systems provide per-tick fixed offsets (TransformProperties: pos/rot/scale) that are not interpolated. The fixed part is applied immediately; only the residual is smoothed. This keeps the graphical object aligned under external forces and removes drift/jitter. Additionally, a target-based registry allows pushing the same offset to all smoothers bound to the same target.
Summary
Add per-tick fixed offsets (TransformProperties: position/rotation/scale) that are not interpolated. The fixed part is applied immediately within the same tick; only the residual is smoothed. Also add a NetworkObject registry to fetch all smoothers bound to a given TargetTransform.
Changes
UniversalTickSmoother
Per-tick ring buffer of fixed offsets as TransformProperties.
AddFixedOffset(uint tick, in TransformProperties delta) to accumulate world-space deltas.
In OnPostTick:
Clamp fixed delta against total tick delta (AxiswiseClamp).
Apply clamped part immediately to graphics (partial snap).
Enqueue goal with the clamped part subtracted (smooth only the residual).
Recompute MoveRates after the snap to avoid pullback/jitter.
AxiswiseClamp(TransformProperties fixed, TransformProperties total)
Position/Scale: per-axis clamp (preserve sign; don’t exceed magnitude).
Rotation: clamp along total’s axis (angle-axis projection + signed magnitude clamp).
TickSmootherController ↔ NetworkObject
NetworkObject
Minimal API
Example of usage
this methods i call only in code which executing in OnTick, so all FixedOffsets will apply after that in OnPostTick
Files changed
UniversalTickSmoother.cs
TickSmootherController.cs
NetworkObject.Prediction.cs
TransformProperties.cs