using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; [NetworkSettings(channel = 0, sendInterval = 0)] public class CarClient : NetworkBehaviour { public float delay = 2; private float timeAlive = 0; private StateBuffer stateBuffer; public GameObject brakeLights; public Transform explosion; public Transform fire; private Transform thisFire; private bool alreadyOnFire = false; private bool isDead = false; private void Start () { if (isClient) { } } public void FixedUpdate () { if(isClient) { if(stateBuffer == null) stateBuffer = new StateBuffer(delay); if (stateBuffer.CanStartInterpolating()) { //Pontosabb, mint a Time.time timeAlive += Time.deltaTime; stateBuffer.Interpolate(timeAlive); if (isDead) { transform.position = Vector3.zero; return; } ChangeBrakeLights(stateBuffer.IsBraking()); if(stateBuffer.ShouldExplode() && !alreadyOnFire) { thisFire = Instantiate(fire, transform.position, transform.rotation); alreadyOnFire = true; } if (stateBuffer.ShouldDie()) { Instantiate(explosion, transform.position, transform.rotation); Destroy(thisFire.gameObject); isDead = true; } transform.position = stateBuffer.GetCurrentPosition(); transform.rotation = stateBuffer.GetCurrentRotation(); } } } //A féklámpa ki/be kapcsolására private void ChangeBrakeLights (bool newState) { for (int a = 0; a < brakeLights.transform.childCount; a++) brakeLights.transform.GetChild(a).gameObject.SetActive(newState); } //RPC: Remote procedure call, QoS: reliable sequenced [ClientRpc(channel = 0)] public void RpcDecodeBits (ulong newStateBits) { if (stateBuffer == null) stateBuffer = new StateBuffer(delay); CompressedStateFrame compressedStateFrame = new CompressedStateFrame(newStateBits); StateFrame newStateFrame = compressedStateFrame.GetDecodedStateFrame(transform); stateBuffer.AddFrame(ref newStateFrame); } } public class CompressedStateFrame { private ulong stateBits; public CompressedStateFrame(ulong stateBits) { this.stateBits = stateBits; } public StateFrame GetDecodedStateFrame(Transform carTransform) { StateFrame decodedFrame = new StateFrame(); decodedFrame.isBraking = TakeAwayLastBitAsBool(); float xPosition = TakeAwayLastNbitsAsFixed(12, 1f / 16); float zPosition = TakeAwayLastNbitsAsFixed(12, 1f / 16); decodedFrame.position = new Vector3(xPosition, carTransform.position.y, zPosition); float rotation = TakeAwayLastNbitsAsFixed(7, 360f / 127); decodedFrame.direction = Quaternion.AngleAxis(rotation, carTransform.up); decodedFrame.shouldExplode = TakeAwayLastBitAsBool(); decodedFrame.shouldDie = TakeAwayLastBitAsBool(); decodedFrame.isTurning = TakeAwayLastBitAsBool(); decodedFrame.serverStartTime = (float)stateBits / 1000; return decodedFrame; } private bool TakeAwayLastBitAsBool () { bool lastBit = ( stateBits & 1 ) == 1; stateBits >>= 1; return lastBit; } private float TakeAwayLastNbitsAsFixed (int n, float multiplier) { return (float)TakeAwayLastNbitsAsUlong(n) * multiplier; } private ulong TakeAwayLastNbitsAsUlong (int n) { ulong mask = (ulong)( 1 << n ) - 1; ulong lastNbits = stateBits & mask; stateBits >>= n; return lastNbits; } } //Egy autó egy állapota, tekinthetőek keyframeeknek, amik között lineárisan készítünk kitöltő állapotokat készítünk [System.Serializable] public class StateFrame { //client mikor kezdte el? public float clientStartTime; //szerver mikor kezdte el? public float serverStartTime; //Az időket az adott autó spawnolásától mérjük. public Vector3 position; public Quaternion direction; public bool isBraking; public bool isTurning; public bool shouldExplode; public bool shouldDie; } //Az autó állapotframejei itt tárolódnak [System.Serializable] public class StateBuffer { private float timeOfLastUpdate; private float delay; private float intervalLengthClient; private float intervalLengthServer; private float qBetweenFrames; private bool isInValidState = true; private bool hasStarted = false; //jelenleg ez a két frame közt van private StateFrame before; private StateFrame after; //Egy autó jövője delay mp-re előre private Queue next; public StateBuffer (float delay) { this.delay = delay; next = new Queue(); } public void AddFrame (ref StateFrame newFrame) { //A visszajátszás sebessége kiszámolásáshoz fontos tudni timeOfLastUpdate = newFrame.serverStartTime; if (before == null) { before = newFrame; before.clientStartTime = 0; } else if (after == null) after = newFrame; else next.Enqueue(newFrame); } public bool CanStartInterpolating () { if (before != null && after != null) hasStarted = true; return hasStarted; } public void Interpolate (float timeAlive) { do { isInValidState = true; intervalLengthClient = timeAlive - before.clientStartTime; intervalLengthServer = after.serverStartTime - before.serverStartTime; CompansateIfOutOfSync(); if (NeedToChangeFrame()) { try { ChangeFrame(timeAlive); } catch (System.Exception) { isInValidState = false; return; } } } while (NeedToChangeFrame()); qBetweenFrames = intervalLengthClient / intervalLengthServer; } private void CompansateIfOutOfSync() { intervalLengthServer /= ( timeOfLastUpdate - before.serverStartTime ) / delay; } private bool NeedToChangeFrame() { return intervalLengthClient > intervalLengthServer || intervalLengthServer == 0; } private void ChangeFrame (float timeAlive) { before = after; before.clientStartTime = timeAlive - ( intervalLengthClient - intervalLengthServer ); after = (StateFrame)next.Dequeue(); } public bool ShouldExplode() { return before.shouldExplode; } public bool ShouldDie() { return before.shouldDie; } public bool IsBraking () { return before.isBraking; } public Vector3 GetCurrentPosition() { if (!isInValidState) return Vector3.zero; if (Vector3.Magnitude(after.position - before.position) > 20) { Debug.Log("jump " + before.position + ", " + after.position); return after.position; } if (before.isTurning) return Vector3.Slerp(before.position, after.position, qBetweenFrames); else return Vector3.Lerp(before.position, after.position, qBetweenFrames); } public Quaternion GetCurrentRotation() { if (!isInValidState) return Quaternion.identity; return Quaternion.Slerp(before.direction, after.direction, qBetweenFrames); } }