Skip to content

Instantly share code, notes, and snippets.

@hellos3b
Last active February 1, 2020 00:14
Show Gist options
  • Save hellos3b/b0d5284a16c3f7ff1a350843063c78be to your computer and use it in GitHub Desktop.
Save hellos3b/b0d5284a16c3f7ff1a350843063c78be to your computer and use it in GitHub Desktop.
Custom network smoothing using uNet in Unity3D
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using ThirdPersonCamera;
/// <summary>
/// Sync and Smooth out player's movement
/// </summary>
public class PlayerSyncTransform : NetworkBehaviour {
[HideInInspector]
public NetworkInstanceId connection_id;
[Header("Network Information")]
public float updateInterval = 0.025f;
public float lerpRate = 0.15f;
private float updateTimer = 0;
private PlayerBody body;
private Vector3 ServerPositionLast = new Vector3(0,0,0); // The previous position got from the server
private Vector3 ServerPosition = new Vector3(0,0,0); // The latest position from the server
private Quaternion ServerRotation = new Quaternion(); // The latest rotation from the server
private PlayerMovementController movementController;
void Awake() {
body = GetComponent<PlayerBody>();
movementController = GetComponent<PlayerMovementController>();
}
void Start() {
ServerPosition = transform.position;
}
void FixedUpdate() {
if (isLocalPlayer) {
// Just a timer to send sync at an interval
if (updateTimer <= 0) {
SendSync ();
updateTimer = updateInterval;
} else {
updateTimer -= Time.deltaTime;
}
} else {
SyncSmooth ();
}
}
void SendSync() {
CmdSync (transform.position, movementController.movement, movementController.vSpeed.get(), Timestamp ());
}
[Command]
void CmdSync(Vector3 position, Vector3 vel, float vspeed, int timestamp) {
// "Simulating" here takes some physics variables, and calculates where it would be in (t) seconds
// where (t) is the time it took from server sending the message to client recieving
// Basically, if I'm at (0,0) and am moving right at 1 unit a second, and the server sent the message half a second ago,
// The simulated position is now (0.5,0)
// GetClientDelay() and GetServerDelay() calculates the difference in timestamps to figure out how long the message took
position = movementController.SimulatePosition(GetServerDelay(timestamp), position, vel, vspeed);
vspeed = movementController.SimulateGravity(GetServerDelay(timestamp), vspeed);
RpcSync (position, vel, vspeed, Timestamp ());
}
[ClientRpc]
void RpcSync(Vector3 position, Vector3 vel, float vspeed, int timestamp) {
if (!isLocalPlayer && enabled) {
position = movementController.SimulatePosition(GetClientDelay(timestamp), position, vel, vspeed);
vspeed = movementController.SimulateGravity(GetClientDelay(timestamp), vspeed);
// I update the controller's physics so they continue emulating client-side, to fill in the gap between server messages
movementController.movement = vel;
movementController.vSpeed.Set(vspeed);
ServerPositionLast = ServerPosition;
ServerPosition = position;
}
}
private void SyncSmooth() {
transform.position = Vector3.Lerp (transform.position, ServerPosition, lerpRate);
transform.rotation = Quaternion.Slerp (transform.rotation, ServerRotation, lerpRate);
}
# region Network Delay Utils
private int Timestamp() {
return NetworkTransport.GetNetworkTimestamp ();
}
private float GetClientDelay(int timestamp) {
if (!isLocalPlayer || isServer) {
return 0;
}
byte error;
int delayMS = NetworkTransport.GetRemoteDelayTimeMS (
connectionToServer.hostId,
connectionToServer.connectionId,
timestamp,
out error);
return (float)delayMS / 1000;
}
private float GetServerDelay(int timestamp) {
if (isServer && isLocalPlayer) {
return 0;
}
byte error;
int delayMS = NetworkTransport.GetRemoteDelayTimeMS (
connectionToClient.hostId,
connectionToClient.connectionId,
timestamp,
out error);
return (float)delayMS / 1000;
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment