private void SimulateOffline() { PlayerSimulation.SimulateOffline(_simConfig, _eulerBody, GetInput, 0d, 5d, Time.fixedDeltaTime); transform.position = _eulerBody.State.Position; transform.rotation = _eulerBody.State.Rotation; }
private void Correct(IClock fixedClock) { if (_serverHistory.Count < 1) { Debug.Log("no server history available"); return; } /* Extrapolate last received server state to catch up to client time */ /* Todo: Save massive amounts of calculation by only simulating 1 correction tick per actual physics frame, you dummy * New server state? Do offline sim for however many steps it requires to catch up to local client time instantly. * THEN, then you only have to simulate one tick for each real-time physics tick in order to keep up. Until the new server state arrives. * * So in this method we only tick once * In the receive state handler we tick multiple times to catch up to local client time */ var latency = _lastReceivedStateRTT / 2d; var timeSinceLastReceivedState = fixedClock.CurrentTime - _lastReceivedStateTimestamp; var timeSinceServerState = latency + timeSinceLastReceivedState; var startTime = _lastReceivedStateTimestamp - latency; Debug.Assert(timeSinceServerState > 0.0, "timeSinceServerState > 0: " + timeSinceServerState); _eulerBody.State = _lastServerState; PlayerSimulation.SimulateOffline(_simulation.Config, _eulerBody, t => _inputHistory.Lerp(t), startTime, timeSinceServerState, fixedClock.DeltaTime); _correctedState = _eulerBody.State; /* Calculate error between corrected server state and current client state */ var currentClientState = _simulation.UnityBody.State; const float maxPosError = 2f; const float maxRotError = 45f; _posError = Vector3.Distance(currentClientState.Position, _correctedState.Position) / maxPosError; _rotError = Quaternion.Angle(currentClientState.Rotation, _correctedState.Rotation) / maxRotError; /* Apply corrected state over time, with use of error metrics */ _posErrorSmooth = Mathf.Lerp(_posErrorSmooth, 0.5f + _posError, 1f / _errorSmoothing * fixedClock.DeltaTime); _rotErrorSmooth = Mathf.Lerp(_rotErrorSmooth, 0.5f + _rotError, 1f / _errorSmoothing * fixedClock.DeltaTime); var interpolatedState = RigidbodyExtensions.Lerp( currentClientState, _correctedState, _posErrorSmooth * fixedClock.DeltaTime * _errorCorrectionSpeed, _rotErrorSmooth * fixedClock.DeltaTime * _errorCorrectionSpeed); if (_applyCorrection) { interpolatedState.ApplyTo(_simulation.UnityBody.Rigidbody); } }