Client prediction improved
This commit is contained in:
@@ -15,7 +15,7 @@ public class CameraFollower : MonoBehaviour
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
void FixedUpdate()
|
||||
{
|
||||
if(target==null){return;}
|
||||
transform.position = Vector3.Lerp(transform.position, target.position + offset, smoothness * Time.deltaTime);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mirror;
|
||||
@@ -14,13 +13,15 @@ public class SpaceshipController : NetworkBehaviour
|
||||
|
||||
[Header("Client Prediction")]
|
||||
public SortedDictionary<int, StatePayload> serverStateBuffer = new SortedDictionary<int, StatePayload>();
|
||||
public Vector3 Detour = Vector3.zero;
|
||||
public Quaternion RotationDetour = Quaternion.identity;
|
||||
public float DetourCorrectionFactor = 0.5f;
|
||||
|
||||
public bool showDebugHUD = false;
|
||||
|
||||
|
||||
void Start()
|
||||
{
|
||||
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
if (joystick == null) { joystick = FindObjectOfType<Joystick>(); }
|
||||
@@ -35,6 +36,9 @@ public class SpaceshipController : NetworkBehaviour
|
||||
|
||||
// Update is called once per frame
|
||||
int timeInMillis => (int)(NetworkTime.time * 1000);
|
||||
int roundedTime => Mathf.FloorToInt((float)timeInMillis / 100f) * 100;
|
||||
|
||||
int lastClientUpdateTime = 0;
|
||||
void FixedUpdate()
|
||||
{
|
||||
if (isLocalPlayer)
|
||||
@@ -52,37 +56,59 @@ public class SpaceshipController : NetworkBehaviour
|
||||
body.rotation = Quaternion.Lerp(body.rotation, Quaternion.AngleAxis(angle1 + 90, Vector3.forward), turningSmoothFactor * joyInput.magnitude);
|
||||
}
|
||||
}
|
||||
if(isLocalPlayer){
|
||||
///Diff = rot1 . rot2
|
||||
///1 = rot1 . rot2 / Diff
|
||||
///1 * Diff * Diff = rot1.rot2. Diff
|
||||
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
CmdUpdateJoyInput(joyInput);
|
||||
|
||||
//Fix Detours
|
||||
if (Mathf.Abs(Detour.magnitude) > 0.1f)
|
||||
{
|
||||
Vector3 newPosition = body.position + Detour;
|
||||
Quaternion newRotation = body.rotation * RotationDetour;
|
||||
|
||||
body.position = Vector3.Lerp(body.position, newPosition, (Mathf.Abs(Detour.magnitude) > 0.2f) ? DetourCorrectionFactor * 2 * Detour.magnitude : DetourCorrectionFactor);
|
||||
Detour = newPosition - body.position;
|
||||
body.rotation = Quaternion.Lerp(body.rotation, newRotation, DetourCorrectionFactor * ((joystick.touchDown) ? 0.1f : 1));
|
||||
RotationDetour = Quaternion.Inverse(transform.rotation) * newRotation;
|
||||
}
|
||||
}
|
||||
|
||||
//Fill input and position buffer for client predictions
|
||||
|
||||
|
||||
int lastUpdatedTime = 0;
|
||||
if (timeInMillis % 32 == 0 && lastUpdatedTime != timeInMillis)
|
||||
{
|
||||
lastUpdatedTime = timeInMillis;
|
||||
StatePayload currentState = new StatePayload(timeInMillis, transform.position, transform.rotation, joyInput);
|
||||
StatePayload currentState = new StatePayload(timeInMillis, transform.position, transform.rotation, joyInput);
|
||||
|
||||
if (isLocalPlayer)
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
if (lastClientUpdateTime < roundedTime)
|
||||
{
|
||||
CmdValidateMovement(timeInMillis, currentState.Position, currentState.Rotation, currentState.Input);
|
||||
CmdValidateMovement(roundedTime, currentState.Position, currentState.Rotation, currentState.Input);
|
||||
lastClientUpdateTime = roundedTime;
|
||||
}
|
||||
if (isServer)
|
||||
}
|
||||
if (isServer)
|
||||
{
|
||||
int lastUpdatedTime = serverStateBuffer.Count > 0 ? serverStateBuffer.Keys.Last() : 0;
|
||||
if (timeInMillis >= lastUpdatedTime + 100)
|
||||
{
|
||||
if (!serverStateBuffer.ContainsKey(timeInMillis))
|
||||
serverStateBuffer.Add(roundedTime, currentState);
|
||||
if (serverStateBuffer.Count > 1024)
|
||||
{
|
||||
serverStateBuffer.Add(timeInMillis, currentState);
|
||||
serverStateBuffer.Remove(serverStateBuffer.Keys.First());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Command]
|
||||
void CmdUpdateJoyInput(Vector2 input){
|
||||
void CmdUpdateJoyInput(Vector2 input)
|
||||
{
|
||||
joyInput = input;
|
||||
}
|
||||
|
||||
@@ -90,46 +116,81 @@ public class SpaceshipController : NetworkBehaviour
|
||||
void CmdValidateMovement(int time, Vector3 position, Quaternion rotation, Vector2 input)
|
||||
{
|
||||
StatePayload payload = new StatePayload(time, position, rotation, input);
|
||||
StatePayload lastServerState = serverStateBuffer.Values.Last();
|
||||
StatePayload payloadToCompare;
|
||||
if (serverStateBuffer.Count < 1) { Debug.Log("still Initiating server buffer"); return; }
|
||||
joyInput = payload.Input;
|
||||
if (!serverStateBuffer.ContainsKey(time))
|
||||
{
|
||||
payloadToCompare = lastServerState;
|
||||
if (lastServerState.Time < time)
|
||||
{
|
||||
Debug.Log("Server doesn't have that data yet\nYou asked for " + time + " best I can do is " + lastServerState.Time);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
if (serverStateBuffer.ContainsKey(time - i))
|
||||
{
|
||||
payloadToCompare = serverStateBuffer[time - i];
|
||||
Debug.Log($"Found closest state at {payloadToCompare.Time}, requested {time}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!serverStateBuffer.ContainsKey(time)){Debug.Log("Server doesn't have that data yet");return;}
|
||||
}
|
||||
else
|
||||
{
|
||||
payloadToCompare = serverStateBuffer[time];
|
||||
}
|
||||
Vector3 positionDiff = payloadToCompare.Position - payload.Position;
|
||||
int timeDiff = lastServerState.Time - time;
|
||||
|
||||
if (serverStateBuffer[time] == payload)
|
||||
if (Mathf.Abs(positionDiff.magnitude) < 0.2f && lastServerState.Time - time < 500)
|
||||
{
|
||||
//Validated, move on
|
||||
}
|
||||
else
|
||||
{
|
||||
RpcRubberBand(serverStateBuffer.Values.Last().Position,serverStateBuffer.Values.Last().Rotation, NetworkTime.time);
|
||||
{
|
||||
|
||||
|
||||
Debug.Log($"RB : {positionDiff.magnitude} [{timeDiff}ms]");
|
||||
RpcRubberBand(serverStateBuffer.Values.Last().Position, serverStateBuffer.Values.Last().Rotation, timeInMillis);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ValidateCSBuffer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
double lastRubberBandTime = 0;
|
||||
[ClientRpc]
|
||||
void RpcRubberBand(Vector3 position, Quaternion rotation, double sentTime)
|
||||
{
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
|
||||
if (sentTime < lastRubberBandTime) { Debug.Log("Old rubber band rpc, ignoree..."); return; }
|
||||
//Lag comprehension
|
||||
double delay = NetworkTime.time - sentTime;
|
||||
transform.position = position;
|
||||
transform.Translate(new Vector3(0, movingSpeed * ((float)delay / 0.02f)));
|
||||
transform.rotation = rotation;
|
||||
double delay = timeInMillis - sentTime + 10;
|
||||
Vector3 newPosition = position + new Vector3(0, movingSpeed * ((float)(delay) / 20f));
|
||||
Detour = newPosition - transform.position;
|
||||
|
||||
// RotationDetour = rotation;
|
||||
RotationDetour = Quaternion.Inverse(transform.rotation) * rotation;
|
||||
lastRubberBandTime = sentTime;
|
||||
|
||||
Debug.Log($"Rubber banded (Detour of {Detour}) you to {transform.position}, @ {sentTime} (delay: {delay}");
|
||||
}
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (!isLocalPlayer) { return; }
|
||||
if (showDebugHUD)
|
||||
{
|
||||
GUI.Label(new Rect(Screen.width - 120, 10, 100, 20), transform.position.ToString());
|
||||
GUI.Label(new Rect(Screen.width - 120, 30, 100, 20), timeInMillis.ToString());
|
||||
GUI.Label(new Rect(Screen.width - 100, Screen.height - 30, 50, 20), (NetworkTime.rtt * 1000).ToString() + " ms");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user