UPF/Assets/Game/Scripts/Minigame/SpaceshipController.cs

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;
}
}