171 lines
4.6 KiB
C#
171 lines
4.6 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Mirror;
|
|
using System.Linq;
|
|
|
|
public class SpaceshipController : NetworkBehaviour
|
|
{
|
|
public Transform body;
|
|
public float movingSpeed = 0.1f;
|
|
public float turningSmoothFactor = 0.1f;
|
|
public Joystick joystick;
|
|
public Vector2 joyInput;
|
|
|
|
[Header("Client Prediction")]
|
|
public SortedDictionary<int, StatePayload> serverStateBuffer = new SortedDictionary<int, StatePayload>();
|
|
|
|
public bool showDebugHUD = false;
|
|
|
|
|
|
void Start()
|
|
{
|
|
|
|
if (isLocalPlayer)
|
|
{
|
|
if (joystick == null) { joystick = FindObjectOfType<Joystick>(); }
|
|
FindObjectOfType<CameraFollower>().SetTarget(transform);
|
|
GetComponent<NetworkTransform>().enabled = false;
|
|
}
|
|
if (isServer)
|
|
{
|
|
GetComponent<NetworkTransform>().enabled = true;
|
|
}
|
|
}
|
|
|
|
// Update is called once per frame
|
|
int timeInMillis => (int)(NetworkTime.time * 1000);
|
|
void FixedUpdate()
|
|
{
|
|
if (isLocalPlayer)
|
|
{
|
|
joyInput = joystick.input;
|
|
}
|
|
//Simulate on both client and server
|
|
if (isLocalPlayer || isServer)
|
|
{
|
|
body.Translate(new Vector3(0, movingSpeed), body);
|
|
if (joyInput != Vector2.zero)
|
|
{
|
|
//Turn
|
|
var angle1 = Mathf.Atan2(-joyInput.y, -joyInput.x) * Mathf.Rad2Deg;
|
|
body.rotation = Quaternion.Lerp(body.rotation, Quaternion.AngleAxis(angle1 + 90, Vector3.forward), turningSmoothFactor * joyInput.magnitude);
|
|
}
|
|
}
|
|
if(isLocalPlayer){
|
|
CmdUpdateJoyInput(joyInput);
|
|
}
|
|
|
|
//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);
|
|
|
|
if (isLocalPlayer)
|
|
{
|
|
CmdValidateMovement(timeInMillis, currentState.Position, currentState.Rotation, currentState.Input);
|
|
}
|
|
if (isServer)
|
|
{
|
|
if (!serverStateBuffer.ContainsKey(timeInMillis))
|
|
{
|
|
serverStateBuffer.Add(timeInMillis, currentState);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
[Command]
|
|
void CmdUpdateJoyInput(Vector2 input){
|
|
joyInput = input;
|
|
}
|
|
|
|
[Command]
|
|
void CmdValidateMovement(int time, Vector3 position, Quaternion rotation, Vector2 input)
|
|
{
|
|
StatePayload payload = new StatePayload(time, position, rotation, input);
|
|
joyInput = payload.Input;
|
|
|
|
if(!serverStateBuffer.ContainsKey(time)){Debug.Log("Server doesn't have that data yet");return;}
|
|
|
|
if (serverStateBuffer[time] == payload)
|
|
{
|
|
//Validated, move on
|
|
}
|
|
else
|
|
{
|
|
RpcRubberBand(serverStateBuffer.Values.Last().Position,serverStateBuffer.Values.Last().Rotation, NetworkTime.time);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void ValidateCSBuffer()
|
|
{
|
|
|
|
}
|
|
|
|
[ClientRpc]
|
|
void RpcRubberBand(Vector3 position, Quaternion rotation, double sentTime)
|
|
{
|
|
if (isLocalPlayer)
|
|
{
|
|
//Lag comprehension
|
|
double delay = NetworkTime.time - sentTime;
|
|
transform.position = position;
|
|
transform.Translate(new Vector3(0, movingSpeed * ((float)delay / 0.02f)));
|
|
transform.rotation = rotation;
|
|
}
|
|
}
|
|
|
|
void OnGUI()
|
|
{
|
|
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());
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public float Angle(Vector2 vector2)
|
|
{
|
|
return 360 - (Mathf.Atan2(vector2.x, vector2.y) * Mathf.Rad2Deg * Mathf.Sign(vector2.x));
|
|
}
|
|
|
|
|
|
//Auto assign default variables [Editor only]
|
|
void OnValidate()
|
|
{
|
|
if (body == null)
|
|
{
|
|
body = transform;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public class StatePayload
|
|
{
|
|
public int Time;
|
|
public Vector3 Position;
|
|
public Quaternion Rotation;
|
|
public Vector2 Input;
|
|
|
|
public StatePayload(int time, Vector3 position, Quaternion rotation, Vector2 input)
|
|
{
|
|
Time = time;
|
|
Position = position;
|
|
Rotation = rotation;
|
|
Input = input;
|
|
}
|
|
} |