UnityTrafficSimulation/UnityProject/Assets/Scripts/CarClient.cs

294 lines
6.8 KiB
C#

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