private void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info) { Vector3 position = observedTransform.position; Quaternion rotation = observedTransform.rotation; int animFlagIndex = -1; if (stream.isWriting) // Executed by owner of the network view { stream.Serialize(ref position); stream.Serialize(ref rotation); if (sendAnimationFlags == true) { if (controllerScript.animatorStates.Any(s => s.Value.State == true)) { var animState = controllerScript.animatorStates.First(s => s.Value.State == true); animFlagIndex = animState.Key; } } stream.Serialize(ref animFlagIndex); } else // Executed by everyone else receiving the data { stream.Serialize(ref position); stream.Serialize(ref rotation); stream.Serialize(ref animFlagIndex); //Shift buffer for (int i = stateBuffer.Length - 1; i >= 1; i--) { stateBuffer[i] = stateBuffer[i - 1]; } // Insert latest data at the front of buffer stateBuffer[0] = new PlayerPayload() { Position = position, Rotation = rotation, ActiveAnimatorFlagIndex = animFlagIndex, Timestamp = (float)info.timestamp }; } }
// Update the Player basing on data received from the server private void Update() { if (!networkView.isMine && Network.connections.Length > 0) // If this is remote side receiving the data and connection exists { #region RemoteClientReplication if (Network.isClient) { clientPing = (Network.GetAveragePing(Network.connections[0]) / 100) + pingMargin; // on client the only connection [0] is the server float interpolationTime = (float)Network.time - clientPing; // make sure there is at least one entry in the buffer if (stateBuffer[0] == null) { stateBuffer[0] = new PlayerPayload() { Position = observedTransform.position, Rotation = observedTransform.rotation, ActiveAnimatorFlagIndex = -1, Timestamp = 0 }; } // Interpolation if (stateBuffer[0].Timestamp > interpolationTime) { for (int i = 0; i < stateBuffer.Length; i++) { if (stateBuffer[i] == null) { continue; } // Find best fitting state or use the last one available if (stateBuffer[i].Timestamp <= interpolationTime || i == stateBuffer.Length - 1) { PlayerPayload bestTarget = stateBuffer[Mathf.Max(i - 1, 0)]; PlayerPayload bestStart = stateBuffer[i]; float timeDiff = bestTarget.Timestamp - bestStart.Timestamp; float lerpTime = 0f; if (timeDiff > 0.0001) { lerpTime = ((interpolationTime - bestStart.Timestamp) / timeDiff); } observedTransform.position = Vector3.Lerp(bestStart.Position, bestTarget.Position, lerpTime); observedTransform.rotation = Quaternion.Slerp(bestStart.Rotation, bestTarget.Rotation, lerpTime); controllerScript.CurrentAnimationFlag(bestTarget.ActiveAnimatorFlagIndex); return; } } } else { //Extrapolation float extrapolationTime = (interpolationTime - stateBuffer[0].Timestamp); if (stateBuffer[0] != null && stateBuffer[1] != null) { PlayerPayload lastSample = stateBuffer[0]; PlayerPayload prevSample = stateBuffer[1]; float timeDiff = lastSample.Timestamp - prevSample.Timestamp; float lerpTime = 0f; if (timeDiff > 0.0001) { lerpTime = ((extrapolationTime - lastSample.Timestamp) / timeDiff); } Vector3 predictedPosition = lastSample.Position + prevSample.Position; observedTransform.position = Vector3.Lerp(lastSample.Position, predictedPosition, lerpTime); observedTransform.rotation = lastSample.Rotation; controllerScript.CurrentAnimationFlag(lastSample.ActiveAnimatorFlagIndex); } // Updates to latest state - no extrapolation //PlayerPayload latest = stateBuffer[0]; //observedTransform.position = Vector3.Lerp(observedTransform.position, latest.Position, 0.5f); //observedTransform.rotation = Quaternion.Slerp(observedTransform.rotation, latest.Rotation, 0.5f); //controllerScript.CurrentAnimationFlag(latest.ActiveAnimatorFlagIndex); } } #endregion #region ServerReplication else if (Network.isServer) { PlayerPayload latest = stateBuffer[0]; // Check if we managed to get a PlayerPayLoad. if (latest != null) { observedTransform.position = latest.Position; observedTransform.rotation = latest.Rotation; // Ignore the animation as it has no bearing on server behaviour } } #endregion } }