@@ -106,44 +106,64 @@ public void Move(InputList inputs, bool replay = false)
106106 }
107107 }
108108
109- public override void OnNetworkSpawn ( )
109+ public override void OnReanticipate ( double lastRoundTripTime )
110110 {
111- MyTransform . OnReanticipate = ( networkTransform , anticipatedValue , anticipationTime , authorityValue , authorityTime ) =>
111+ // Have to store the transform's previous state because calls to AnticipateMove() and
112+ // AnticipateRotate() will overwrite it.
113+ var previousState = MyTransform . PreviousAnticipatedState ;
114+
115+ var authorityTime = NetworkManager . LocalTime . Time - lastRoundTripTime ;
116+ // Here we re-anticipate the new position of the player based on the updated server position.
117+ // We do this by taking the current authoritative position and replaying every input we have received
118+ // since the reported authority time, re-applying all the movement we have applied since then
119+ // to arrive at a new anticipated player location.
120+
121+ foreach ( var item in InputManager . GetHistory ( ) )
112122 {
113- // Here we re-anticipate the new position of the player based on the updated server position.
114- // We do this by taking the current authoritative position and replaying every input we have received
115- // since the reported authority time, re-applying all the movement we have applied since then
116- // to arrive at a new anticipated player location.
117- foreach ( var item in InputManager . GetHistory ( ) )
123+ if ( item . Time <= authorityTime )
118124 {
119- if ( item . Time <= authorityTime )
120- {
121- continue ;
122- }
125+ continue ;
126+ }
123127
124- Move ( item . Item , true ) ;
128+ Move ( item . Item , true ) ;
129+ }
130+ // Clear out all the input history before the given authority time. We don't need anything before that
131+ // anymore as we won't get any more updates from the server from before this one. We keep the current
132+ // authority time because theoretically another system may need that.
133+ InputManager . RemoveBefore ( authorityTime ) ;
134+ // It's not always desirable to smooth the transform. In cases of very large discrepencies in state,
135+ // it can sometimes be desirable to simply teleport to the new position. We use the SmoothDistance
136+ // value (and use SqrMagnitude instead of Distance for efficiency) as a threshold for teleportation.
137+ // This could also use other mechanisms of detection: For example, when the Telport input is included
138+ // in the replay set, we could set a flag to disable smoothing because we know we are teleporting.
139+ if ( SmoothTime != 0.0 )
140+ {
141+ var sqDist = Vector3 . SqrMagnitude ( previousState . Position - MyTransform . AnticipatedState . Position ) ;
142+ if ( sqDist <= 0.25 * 0.25 )
143+ {
144+ // This prevents small amounts of wobble from slight differences.
145+ MyTransform . AnticipateState ( previousState ) ;
125146 }
126- // Clear out all the input history before the given authority time. We don't need anything before that
127- // anymore as we won't get any more updates from the server from before this one. We keep the current
128- // authority time because theoretically another system may need that.
129- InputManager . RemoveBefore ( authorityTime ) ;
130- // It's not always desirable to smooth the transform. In cases of very large discrepencies in state,
131- // it can sometimes be desirable to simply teleport to the new position. We use the SmoothDistance
132- // value (and use SqrMagnitude instead of Distance for efficiency) as a threshold for teleportation.
133- // This could also use other mechanisms of detection: For example, when the Telport input is included
134- // in the replay set, we could set a flag to disable smoothing because we know we are teleporting.
135- if ( SmoothTime != 0.0 && Vector3 . SqrMagnitude ( anticipatedValue . Position - networkTransform . AnticipatedState . Position ) < SmoothDistance * SmoothDistance )
147+ else if ( sqDist < SmoothDistance * SmoothDistance )
136148 {
137149 // Server updates are not necessarily smooth, so applying reanticipation can also result in
138150 // hitchy, unsmooth animations. To compensate for that, we call this to smooth from the previous
139151 // anticipated state (stored in "anticipatedValue") to the new state (which, because we have used
140152 // the "Move" method that updates the anticipated state of the transform, is now the current
141153 // transform anticipated state)
142- networkTransform . Smooth ( anticipatedValue , networkTransform . AnticipatedState , SmoothTime ) ;
154+ MyTransform . Smooth ( previousState , MyTransform . AnticipatedState , SmoothTime ) ;
143155 }
144- } ;
145- base . OnNetworkSpawn ( ) ;
156+ }
157+
158+ }
146159
160+ /// <summary>
161+ /// When we apply changes to the latency and jitter, it respawns everything.
162+ /// We want to make sure there's no input left over from before that by clearing it.
163+ /// </summary>
164+ public override void OnNetworkSpawn ( )
165+ {
166+ InputManager . Clear ( ) ;
147167 }
148168
149169 /// <summary>
@@ -162,16 +182,16 @@ private void ServerMoveRpc(InputList inputs)
162182 // just reuse the same method here with no problem.
163183 Move ( inputs ) ;
164184 // Server can use Smoothing for interpolation purposes as well.
165- MyTransform . Smooth ( currentPosition , MyTransform . AuthorityState , SmoothTime ) ;
185+ MyTransform . Smooth ( currentPosition , MyTransform . AuthoritativeState , SmoothTime ) ;
166186 }
167187
168188 public void Update ( )
169189 {
170190 // The "ghost transform" here is a little smaller player object that shows the current authority position,
171191 // which is a few frames behind our anticipated value. This helps render the difference.
172- GhostTrasform . position = MyTransform . AuthorityState . Position ;
173- GhostTrasform . rotation = MyTransform . AuthorityState . Rotation ;
174- GhostTrasform . localScale = MyTransform . AuthorityState . Scale * 0.75f ;
192+ GhostTrasform . position = MyTransform . AuthoritativeState . Position ;
193+ GhostTrasform . rotation = MyTransform . AuthoritativeState . Rotation ;
194+ GhostTrasform . localScale = MyTransform . AuthoritativeState . Scale * 0.75f ;
175195 }
176196
177197 // Input processing happens in FixedUpdate rather than Update because the frame rate of server and client
0 commit comments