123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- using System.Collections.Generic;
- using UnityEngine;
- namespace Mirror.Examples.SnapshotInterpolationDemo
- {
- public class ServerCube : MonoBehaviour
- {
- [Header("Components")]
- public ClientCube client;
- [Header("Movement")]
- public float distance = 10;
- public float speed = 3;
- Vector3 start;
- [Header("Snapshot Interpolation")]
- [Tooltip("Send N snapshots per second. Multiples of frame rate make sense.")]
- public int sendRate = 30; // in Hz. easier to work with as int for EMA. easier to display '30' than '0.333333333'
- public float sendInterval => 1f / sendRate;
- float lastSendTime;
- [Header("Latency Simulation")]
- [Tooltip("Latency in seconds")]
- public float latency = 0.05f; // 50 ms
- [Tooltip("Latency jitter, randomly added to latency.")]
- [Range(0, 1)] public float jitter = 0.05f;
- [Tooltip("Packet loss in %")]
- [Range(0, 1)] public float loss = 0.1f;
- [Tooltip("Scramble % of unreliable messages, just like over the real network. Mirror unreliable is unordered.")]
- [Range(0, 1)] public float scramble = 0.1f;
- // random
- // UnityEngine.Random.value is [0, 1] with both upper and lower bounds inclusive
- // but we need the upper bound to be exclusive, so using System.Random instead.
- // => NextDouble() is NEVER < 0 so loss=0 never drops!
- // => NextDouble() is ALWAYS < 1 so loss=1 always drops!
- System.Random random = new System.Random();
- // hold on to snapshots for a little while before delivering
- // <deliveryTime, snapshot>
- List<(double, Snapshot3D)> queue = new List<(double, Snapshot3D)>();
- // latency simulation:
- // always a fixed value + some jitter.
- float SimulateLatency() => latency + Random.value * jitter;
- void Start()
- {
- start = transform.position;
- }
- void Update()
- {
- // move on XY plane
- float x = Mathf.PingPong(Time.time * speed, distance);
- transform.position = new Vector3(start.x + x, start.y, start.z);
- // broadcast snapshots every interval
- if (Time.time >= lastSendTime + sendInterval)
- {
- Send(transform.position);
- lastSendTime = Time.time;
- }
- Flush();
- }
- void Send(Vector3 position)
- {
- // create snapshot
- // Unity 2019 doesn't have Time.timeAsDouble yet
- Snapshot3D snap = new Snapshot3D(NetworkTime.localTime, 0, position);
- // simulate packet loss
- bool drop = random.NextDouble() < loss;
- if (!drop)
- {
- // simulate scramble (Random.Next is < max, so +1)
- bool doScramble = random.NextDouble() < scramble;
- int last = queue.Count;
- int index = doScramble ? random.Next(0, last + 1) : last;
- // simulate latency
- float simulatedLatency = SimulateLatency();
- // Unity 2019 doesn't have Time.timeAsDouble yet
- double deliveryTime = NetworkTime.localTime + simulatedLatency;
- queue.Insert(index, (deliveryTime, snap));
- }
- }
- void Flush()
- {
- // flush ready snapshots to client
- for (int i = 0; i < queue.Count; ++i)
- {
- (double deliveryTime, Snapshot3D snap) = queue[i];
- // Unity 2019 doesn't have Time.timeAsDouble yet
- if (NetworkTime.localTime >= deliveryTime)
- {
- client.OnMessage(snap);
- queue.RemoveAt(i);
- --i;
- }
- }
- }
- }
- }
|