Code: Select all
using UnityEngine;
using System.Collections;
using System;
using Sfs2X;
using Sfs2X.Core;
using Sfs2X.Entities;
using Sfs2X.Entities.Data;
using Sfs2X.Requests;
using Sfs2X.Logging;
public class NetworkTransformReceiver : MonoBehaviour {
public float yAdjust = 0.0f; // Ajust y position when synchronizing the local and remote models.
public float interpolationPeriod = 0.1f; // This value should be equal to the sendingPerion value of the Sender script
private bool receiveMode = false;
private NetworkTransform lastState; // Last received transform state
private NetworkTransform interpolateTo = null; // Last state we interpolate to in receiving mode.
private NetworkTransform interpolateFrom; // Point from which to start interpolation
private int interpolationPoint = 0; // Current interpolation point
private int maxInterpolationPoints = 0; // Maximum number of interpolation points;
private float interpolationDelta = 0; // Delta value by which interpolate
private FPSStorage fpsStorage;
private Queue queue = new Queue(); // Queue to store transform states for interpolations
// We call it on remote player to start receiving his transform
void StartReceiving() {
lastState = new NetworkTransform(this.gameObject);
fpsStorage = GameObject.Find("FPS").GetComponent(typeof(FPSStorage)) as FPSStorage;
receiveMode = true;
}
void Update() {
if (receiveMode) {
InterpolateTransform();
}
}
//This method is called when receiving remote transform
// We update lastState here to know last received transform state
void ReceiveTransform(SFSObject data) {
if (receiveMode) {
Vector3 pos = new Vector3(Convert.ToSingle(data.GetDouble("x")),
Convert.ToSingle(data.GetDouble("y"))+yAdjust,
Convert.ToSingle(data.GetDouble("z"))
);
Quaternion rot = new Quaternion(Convert.ToSingle(data.GetDouble("rx")),
Convert.ToSingle(data.GetDouble("ry")),
Convert.ToSingle(data.GetDouble("rz")),
Convert.ToSingle(data.GetDouble("w"))
);
lastState.InitFromValues(pos, rot);
// Adding next received state to the queue
NetworkTransform nextState = new NetworkTransform(this.gameObject);
nextState.InitFromValues(pos, rot);
queue.Enqueue(nextState);
}
}
// This method is called in every Fixed Update in receiving mode. And it does transform interpolation to the latest state.
void InterpolateTransform() {
// If interpolationg
if (interpolationPoint < maxInterpolationPoints) {
interpolationPoint++;
float t = interpolationPoint*interpolationDelta;
if (t>1) t=1;
transform.position = Vector3.Lerp(interpolateFrom.position, interpolateTo.position, t);
transform.rotation = Quaternion.Slerp(interpolateFrom.rotation, interpolateTo.rotation, t);
}
else {
// Finished interpolating to the next point
if (interpolateTo!=null) {
// Fixing interpolation result to set transform right to the next point
transform.position = interpolateTo.position;
transform.rotation = interpolateTo.rotation;
}
// Take new value from queue
if (queue.Count!=0) {
NetworkTransform nextTransform = queue.Dequeue() as NetworkTransform;
//Start interpolation to the next transform
// Set new final interpolation state and reset interpolationPoint
interpolateTo = nextTransform;
// Set new point from which to start interpolation as current transform
interpolateFrom = new NetworkTransform(this.gameObject);
interpolationPoint = 0;
float frameRate = fpsStorage.GetCurrentFPS();
// Calculate the total number of interpolation points as number of frames during interpolationPriod
maxInterpolationPoints = Convert.ToInt32(Math.Round(frameRate * interpolationPeriod));
// Reset interpolation deltaTime
interpolationDelta = 1.0f / Convert.ToSingle(maxInterpolationPoints);
}
else {
// If queue is empty just setting the transform to the last received state
transform.position = lastState.position;
transform.rotation = lastState.rotation;
}
}
}
}
I don't think that there are major changes. First thing is that comment says that InterpolateTransform is called in every FixedUpdate, but in fact it is called in every Update. However, this is not so important.
I noticed that big "lags" can be observed in my application. What is strange, I checked network communication and there are no real lags. After some analysis of the above script I noticed that when other player is moving all the time my queue is growing almost constantly and this produces huge lag. This is strange because during normal usage queue should be almost empty all the time. When player is moving all the time, the new coordinates are sent after each 0,1s (interpolationPeriod set to 0.1f on sender and receiver), but this should not ba a problem. Scripts contains an interpolation which is using fpsStorage.GetCurrentFPS() and a minute ago I noticed that fpsStorage calculates FPS in a strange way. Here is an Update in which this is done:
Code: Select all
function Update()
{
++frames;
var newSample = Time.realtimeSinceStartup;
var deltaTime = newSample - lastSample;
lastSample = newSample;
timeleft -= deltaTime;
accum += 1.0 / deltaTime;
// Interval ended - update GUI text and start new interval
if( timeleft <= 0.0 )
{
// display two fractional digits (f2 format)
fps = accum/frames;
if (fps!=oldFps) {
oldFps = fps;
SendMessage("FPSChanged", fps);
}
guiText.text = fps.ToString("f2");
SendMessage("DisplayFPS", fps.ToString("f2"));
timeleft = updateInterval;
accum = 0.0;
frames = 0;
++gotIntervals;
}
As you can see fps is calculated not as a number of frames per second, but rather as an average value of the momentary FPS values and this in my opinion is wrong, because interpolation mechanism is not working well in specific conditions. For example if we take 2 frames one is displayed for 0.25s and the second one for 0.75s, for me it's obvious that FPS is 2 (2/(0.25 + 0.75)). However, above script will give different result: (1/0.25 + 1/0.75)/2=~2.67. Both methods will give similar values when frame rate is quite constant, but as you can see it can be very different when it's not. Value is higher and iterpolation between each stateis taking more time that it should. This causes queue to grow...
Can somebody please check my theory? Why FPS are calculated in such strange way in this script?