How to program network predication in photon/u3d?
Options
i found article about this :http://www.mmorpg.com/discussion2.cfm/post/2228522#2228522
but no luck,failed to do this in unity3d with photon ,
i hope photon team can do us a favour,please give a small example to show how to use network predication(with remote player smooth position sync.)
this means a lot for those who are using photon make a fps like mmog!!
appreciate!!!
but no luck,failed to do this in unity3d with photon ,
i hope photon team can do us a favour,please give a small example to show how to use network predication(with remote player smooth position sync.)
this means a lot for those who are using photon make a fps like mmog!!
appreciate!!!
0
Comments
-
I would recommend you to download the Unity3D Networking Example from the resource area in the unity3d - support area.
In there you will find a NetworkRigidbody class which does exactly what you are looking for, all you have to do is to replace the OnSerializeNetworkView with sending and receiving through Photon.0 -
thanks for the answer,
the question is:
there are variables :NetworkTime and NetmessageInfo .timeStamp in u3d network functions,
what variables should i use instead ,in Photon ?
GameInstance.peer.RoundTripTime? or use ServerTimeInMilliSeconds ?0 -
timestamp would be the networktime at time of sending which you just put into the hashtable you send.
the NetworkTime is replaced through the time in milliseconds provided by the PhotonPeer / LightPeer
that time is the base of any kind of interpolation, extrapolation and prediction as it ensures that all nodes in the network (server and clients) do their calculation basing on the same time0 -
for networking time i use peer.ServerTimeInMilliSeconds instead,
when the packet sent i record the serverTime ,and when read packet ,get the serverTime as timeSpan for State.
and code will be something like this:
void OnSendMyData(LitePeer peer,Hashtable evInfo)
{
...
evInfo.Add((System.Object)"ts",peer.ServerTimeInMilliSeconds/1000d);
...
}
void OnGetMyData(Hashtable evData)
{
...
State state;
state.timestamp =(double)evData["ts"];
...
}
and in the Update function
....
//usePhoton.serverTime is a static var that keep reading the current peer serverTime.
double interpolationTime = usePhoton.serverTime/1000.0d - m_InterpolationBackTime;
....
Am i right?
(ps. after test, the transform latency still exists,my packet sent rate is about 12-20times/s,u3d network works fine,...can't figure this out!!!)0 -
Why do you break down everything to seconds (by /1000d)? You send 4-byte integers anyways, so you don't have to degrade the precision.
The "transform latency" is what? And m_InterpolationBackTime?
You will need to get the time elapsed since a transform was sent. On the receiving end this would be usePhoton.serverTime - sentTime. And sentTime should simply be peer.ServerTimeInMilliSeconds.0 -
sure,it should be like this:
void OnSendMyData(LitePeer peer,Hashtable evInfo)
{
...
evInfo.Add((System.Object)"ts",peer.ServerTimeInMilliSeconds);
...
}
void OnGetMyData(Hashtable evData)
{
...
State state;
state.timestamp =((int)evData["ts"])/1000d;
...
}
as dreamora said,i used the NetworkRigidBody class in unity network example,
sorry for my english,"transform latency" means the remote player position/rotation was delayed.
m_InterpolationBackTime is a constant for transform interpolation(check out u3d the network example).
because the u3d example uses Seconds for caculation,so i used serverTime/1000,m_InterpolationBack is also Seconds(0.1 in the u3d example)
You will need to get the time elapsed since a transform was sent. On the receiving end this would be usePhoton.serverTime - sentTime. And sentTime should simply be peer.ServerTimeInMilliSeconds.
i got a little confused,
really appreciate if you guys can make a simple example(or a piece of worked code) to show this,that helps a lot!!!
thanks!!0 -
I can't promise we have a sample anytime soon. I would have to check out animations first
You already send the (server)timestamp when the animation was starting by evInfo.Add(). On the receiving end, you set the animation and "advance" it by:
int startTime = (int)evData["ts"];
passedTime = peer.ServerTimeInMilliSeconds - startTime;
There might be still some lag of course, cause the ServerTimeInMilliSeconds is not completely accurate. Is it maybe what you see as lag?0 -
even 2 clients are running on the same machine?
i just used local host for testing,the lag is still obvious.
just curious why this works great with u3d network functions!!!0 -
You have to synchronize with the server timestamp even though you are on the same machine, yes.
Our eyes are pretty good at noticing different movements and Photon (by default) adds ~50ms of lag to accumulate commands sent to each client (to avoid transfer protocol overhead).
If you could attach your code, maybe someone else can share his insights how to get it more accurate.0 -
-
Any help on this subject ?
I am - i think - in the same situation as Liverol .
I wonder if someone has allready made a portage of the networkRigidbody script ?0 -
is this even good ?
[code2=csharp]using UnityEngine;
using System.Collections;
public class NetworkRigidbodyPhoton : Photon.MonoBehaviour {
public double m_InterpolationBackTime = 0.1;
public double m_ExtrapolationLimit = 0.5;
internal struct State
{
internal double timestamp;
internal Vector3 pos;
internal Vector3 velocity;
internal Quaternion rot;
internal Vector3 angularVelocity;
}
// We store twenty states with "playback" information
State[] m_BufferedState = new State[20];
// Keep track of what slots are used
int m_TimestampCount;
//void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info){
// Send data to server
if (stream.isWriting){
Vector3 pos = rigidbody.position;
Quaternion rot = rigidbody.rotation;
Vector3 velocity = rigidbody.velocity;
Vector3 angularVelocity = rigidbody.angularVelocity;
stream.Serialize(ref pos);
stream.Serialize(ref velocity);
stream.Serialize(ref rot);
stream.Serialize(ref angularVelocity);
}
// Read data from remote client
else{
Vector3 pos = Vector3.zero;
Vector3 velocity = Vector3.zero;
Quaternion rot = Quaternion.identity;
Vector3 angularVelocity = Vector3.zero;
stream.Serialize(ref pos);
stream.Serialize(ref velocity);
stream.Serialize(ref rot);
stream.Serialize(ref angularVelocity);
// Shift the buffer sideways, deleting state 20
for (int i=m_BufferedState.Length-1;i>=1;i--)
{
m_BufferedState = m_BufferedState[i-1];
}
// Record current state in slot 0
State state;
state.timestamp = info.timestamp;
state.pos = pos;
state.velocity = velocity;
state.rot = rot;
state.angularVelocity = angularVelocity;
m_BufferedState[0] = state;
// Update used slot count, however never exceed the buffer size
// Slots aren't actually freed so this just makes sure the buffer is
// filled up and that uninitalized slots aren't used.
m_TimestampCount = Mathf.Min(m_TimestampCount + 1, m_BufferedState.Length);
// Check if states are in order, if it is inconsistent you could reshuffel or
// drop the out-of-order state. Nothing is done here
for (int i=0;i<m_TimestampCount-1;i++)
{
if (m_BufferedState.timestamp < m_BufferedState[i+1].timestamp)
Debug.Log("State inconsistent");
}
}
}
// We have a window of interpolationBackTime where we basically play
// By having interpolationBackTime the average ping, you will usually use interpolation.
// And only if no more data arrives we will use extra polation
void Update () {
if (photonView.isMine){return;}
// This is the target playback time of the rigid body
double interpolationTime = PhotonNetwork.time - m_InterpolationBackTime;
// Use interpolation if the target playback time is present in the buffer
if (m_BufferedState[0].timestamp > interpolationTime)
{
// Go through buffer and find correct state to play back
for (int i=0;i<m_TimestampCount;i++)
{
if (m_BufferedState.timestamp <= interpolationTime || i == m_TimestampCount-1)
{
// The state one slot newer (<100ms) than the best playback state
State rhs = m_BufferedState[Mathf.Max(i-1, 0)];
// The best playback state (closest to 100 ms old (default time))
State lhs = m_BufferedState;
// Use the time between the two slots to determine if interpolation is necessary
double length = rhs.timestamp - lhs.timestamp;
float t = 0.0F;
// As the time difference gets closer to 100 ms t gets closer to 1 in
// which case rhs is only used
// Example:
// Time is 10.000, so sampleTime is 9.900
// lhs.time is 9.910 rhs.time is 9.980 length is 0.070
// t is 9.900 - 9.910 / 0.070 = 0.14. So it uses 14% of rhs, 86% of lhs
if (length > 0.0001)
t = (float)((interpolationTime - lhs.timestamp) / length);
// if t=0 => lhs is used directly
transform.localPosition = Vector3.Lerp(lhs.pos, rhs.pos, t);
transform.localRotation = Quaternion.Slerp(lhs.rot, rhs.rot, t);
return;
}
}
}
// Use extrapolation
else
{
State latest = m_BufferedState[0];
float extrapolationLength = (float)(interpolationTime - latest.timestamp);
// Don't extrapolation for more than 500 ms, you would need to do that carefully
if (extrapolationLength < m_ExtrapolationLimit)
{
float axisLength = extrapolationLength * latest.angularVelocity.magnitude * Mathf.Rad2Deg;
Quaternion angularRotation = Quaternion.AngleAxis(axisLength, latest.angularVelocity);
rigidbody.position = latest.pos + latest.velocity * extrapolationLength;
rigidbody.rotation = angularRotation * latest.rot;
rigidbody.velocity = latest.velocity;
rigidbody.angularVelocity = latest.angularVelocity;
}
}
}
}[/code2]0