NetworkTransform: use local position and rotation for VR support. Fixes #779

This commit is contained in:
vis2k 2019-06-04 10:55:20 +02:00
parent cb3d3dba72
commit 36be83d6fd

View File

@ -40,8 +40,9 @@ public enum Compression { None, Much, Lots , NoRotation }; // easily understanda
public class DataPoint public class DataPoint
{ {
public float timeStamp; public float timeStamp;
public Vector3 position; // use local position/rotation for VR support
public Quaternion rotation; public Vector3 localPosition;
public Quaternion localRotation;
public float movementSpeed; public float movementSpeed;
} }
// interpolation start and goal // interpolation start and goal
@ -89,18 +90,19 @@ static void SerializeIntoWriter(NetworkWriter writer, Vector3 position, Quaterni
public override bool OnSerialize(NetworkWriter writer, bool initialState) public override bool OnSerialize(NetworkWriter writer, bool initialState)
{ {
SerializeIntoWriter(writer, targetComponent.transform.position, targetComponent.transform.rotation, compressRotation); // use local position/rotation for VR support
SerializeIntoWriter(writer, targetComponent.transform.localPosition, targetComponent.transform.localRotation, compressRotation);
return true; return true;
} }
// try to estimate movement speed for a data point based on how far it // try to estimate movement speed for a data point based on how far it
// moved since the previous one // moved since the previous one
// => if this is the first time ever then we use our best guess: // => if this is the first time ever then we use our best guess:
// -> delta based on transform.position // -> delta based on transform.localPosition
// -> elapsed based on send interval hoping that it roughly matches // -> elapsed based on send interval hoping that it roughly matches
static float EstimateMovementSpeed(DataPoint from, DataPoint to, Transform transform, float sendInterval) static float EstimateMovementSpeed(DataPoint from, DataPoint to, Transform transform, float sendInterval)
{ {
Vector3 delta = to.position - (from != null ? from.position : transform.position); Vector3 delta = to.localPosition - (from != null ? from.localPosition : transform.localPosition);
float elapsed = from != null ? to.timeStamp - from.timeStamp : sendInterval; float elapsed = from != null ? to.timeStamp - from.timeStamp : sendInterval;
return elapsed > 0 ? delta.magnitude / elapsed : 0; // avoid NaN return elapsed > 0 ? delta.magnitude / elapsed : 0; // avoid NaN
} }
@ -112,7 +114,7 @@ void DeserializeFromReader(NetworkReader reader)
DataPoint temp = new DataPoint DataPoint temp = new DataPoint
{ {
// deserialize position // deserialize position
position = reader.ReadVector3() localPosition = reader.ReadVector3()
}; };
// deserialize rotation // deserialize rotation
@ -122,7 +124,7 @@ void DeserializeFromReader(NetworkReader reader)
float x = reader.ReadSingle(); float x = reader.ReadSingle();
float y = reader.ReadSingle(); float y = reader.ReadSingle();
float z = reader.ReadSingle(); float z = reader.ReadSingle();
temp.rotation = Quaternion.Euler(x, y, z); temp.localRotation = Quaternion.Euler(x, y, z);
} }
else if (compressRotation == Compression.Much) else if (compressRotation == Compression.Much)
{ {
@ -130,13 +132,13 @@ void DeserializeFromReader(NetworkReader reader)
float x = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); float x = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360);
float y = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); float y = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360);
float z = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); float z = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360);
temp.rotation = Quaternion.Euler(x, y, z); temp.localRotation = Quaternion.Euler(x, y, z);
} }
else if (compressRotation == Compression.Lots) else if (compressRotation == Compression.Lots)
{ {
// read 2 byte, 5 bits per float // read 2 byte, 5 bits per float
float[] xyz = FloatBytePacker.UnpackUShortIntoThreeFloats(reader.ReadUInt16(), 0, 360); float[] xyz = FloatBytePacker.UnpackUShortIntoThreeFloats(reader.ReadUInt16(), 0, 360);
temp.rotation = Quaternion.Euler(xyz[0], xyz[1], xyz[2]); temp.localRotation = Quaternion.Euler(xyz[0], xyz[1], xyz[2]);
} }
temp.timeStamp = Time.time; temp.timeStamp = Time.time;
@ -152,8 +154,9 @@ void DeserializeFromReader(NetworkReader reader)
{ {
start = new DataPoint{ start = new DataPoint{
timeStamp = Time.time - syncInterval, timeStamp = Time.time - syncInterval,
position = targetComponent.transform.position, // local position/rotation for VR support
rotation = targetComponent.transform.rotation, localPosition = targetComponent.transform.localPosition,
localRotation = targetComponent.transform.localRotation,
movementSpeed = temp.movementSpeed movementSpeed = temp.movementSpeed
}; };
} }
@ -188,17 +191,19 @@ void DeserializeFromReader(NetworkReader reader)
// //
else else
{ {
float oldDistance = Vector3.Distance(start.position, goal.position); float oldDistance = Vector3.Distance(start.localPosition, goal.localPosition);
float newDistance = Vector3.Distance(goal.position, temp.position); float newDistance = Vector3.Distance(goal.localPosition, temp.localPosition);
start = goal; start = goal;
// teleport / lag / obstacle detection: only continue at current // teleport / lag / obstacle detection: only continue at current
// position if we aren't too far away // position if we aren't too far away
if (Vector3.Distance(targetComponent.transform.position, start.position) < oldDistance + newDistance) //
// // local position/rotation for VR support
if (Vector3.Distance(targetComponent.transform.localPosition, start.localPosition) < oldDistance + newDistance)
{ {
start.position = targetComponent.transform.position; start.localPosition = targetComponent.transform.localPosition;
start.rotation = targetComponent.transform.rotation; start.localRotation = targetComponent.transform.localRotation;
} }
} }
@ -223,7 +228,7 @@ void CmdClientToServerSync(byte[] payload)
// server-only mode does no interpolation to save computations, // server-only mode does no interpolation to save computations,
// but let's set the position directly // but let's set the position directly
if (isServer && !isClient) if (isServer && !isClient)
ApplyPositionAndRotation(goal.position, goal.rotation); ApplyPositionAndRotation(goal.localPosition, goal.localRotation);
// set dirty so that OnSerialize broadcasts it // set dirty so that OnSerialize broadcasts it
SetDirtyBit(1UL); SetDirtyBit(1UL);
@ -258,7 +263,7 @@ static Vector3 InterpolatePosition(DataPoint start, DataPoint goal, Vector3 curr
// -> speed is 0 if we just started after idle, so always use max // -> speed is 0 if we just started after idle, so always use max
// for best results // for best results
float speed = Mathf.Max(start.movementSpeed, goal.movementSpeed); float speed = Mathf.Max(start.movementSpeed, goal.movementSpeed);
return Vector3.MoveTowards(currentPosition, goal.position, speed * Time.deltaTime); return Vector3.MoveTowards(currentPosition, goal.localPosition, speed * Time.deltaTime);
} }
return currentPosition; return currentPosition;
} }
@ -268,7 +273,7 @@ static Quaternion InterpolateRotation(DataPoint start, DataPoint goal, Quaternio
if (start != null) if (start != null)
{ {
float t = CurrentInterpolationFactor(start, goal); float t = CurrentInterpolationFactor(start, goal);
return Quaternion.Slerp(start.rotation, goal.rotation, t); return Quaternion.Slerp(start.localRotation, goal.localRotation, t);
} }
return defaultRotation; return defaultRotation;
} }
@ -292,8 +297,9 @@ bool NeedsTeleport()
bool HasMovedOrRotated() bool HasMovedOrRotated()
{ {
// moved or rotated? // moved or rotated?
bool moved = lastPosition != targetComponent.transform.position; // local position/rotation for VR support
bool rotated = lastRotation != targetComponent.transform.rotation; bool moved = lastPosition != targetComponent.transform.localPosition;
bool rotated = lastRotation != targetComponent.transform.localRotation;
// save last for next frame to compare // save last for next frame to compare
// (only if change was detected. otherwise slow moving objects might // (only if change was detected. otherwise slow moving objects might
@ -302,8 +308,9 @@ bool HasMovedOrRotated()
bool change = moved || rotated; bool change = moved || rotated;
if (change) if (change)
{ {
lastPosition = targetComponent.transform.position; // local position/rotation for VR support
lastRotation = targetComponent.transform.rotation; lastPosition = targetComponent.transform.localPosition;
lastRotation = targetComponent.transform.localRotation;
} }
return change; return change;
} }
@ -311,10 +318,11 @@ bool HasMovedOrRotated()
// set position carefully depending on the target component // set position carefully depending on the target component
void ApplyPositionAndRotation(Vector3 position, Quaternion rotation) void ApplyPositionAndRotation(Vector3 position, Quaternion rotation)
{ {
targetComponent.transform.position = position; // local position/rotation for VR support
targetComponent.transform.localPosition = position;
if (Compression.NoRotation != compressRotation) if (Compression.NoRotation != compressRotation)
{ {
targetComponent.transform.rotation = rotation; targetComponent.transform.localRotation = rotation;
} }
} }
@ -341,8 +349,9 @@ void Update()
if (HasMovedOrRotated()) if (HasMovedOrRotated())
{ {
// serialize // serialize
// local position/rotation for VR support
NetworkWriter writer = new NetworkWriter(); NetworkWriter writer = new NetworkWriter();
SerializeIntoWriter(writer, targetComponent.transform.position, targetComponent.transform.rotation, compressRotation); SerializeIntoWriter(writer, targetComponent.transform.localPosition, targetComponent.transform.localRotation, compressRotation);
// send to server // send to server
CmdClientToServerSync(writer.ToArray()); CmdClientToServerSync(writer.ToArray());
@ -362,12 +371,14 @@ void Update()
// teleport or interpolate // teleport or interpolate
if (NeedsTeleport()) if (NeedsTeleport())
{ {
ApplyPositionAndRotation(goal.position, goal.rotation); // local position/rotation for VR support
ApplyPositionAndRotation(goal.localPosition, goal.localRotation);
} }
else else
{ {
ApplyPositionAndRotation(InterpolatePosition(start, goal, targetComponent.transform.position), // local position/rotation for VR support
InterpolateRotation(start, goal, targetComponent.transform.rotation)); ApplyPositionAndRotation(InterpolatePosition(start, goal, targetComponent.transform.localPosition),
InterpolateRotation(start, goal, targetComponent.transform.localRotation));
} }
} }
} }
@ -376,26 +387,26 @@ void Update()
static void DrawDataPointGizmo(DataPoint data, Color color) static void DrawDataPointGizmo(DataPoint data, Color color)
{ {
// use a little offset because transform.position might be in // use a little offset because transform.localPosition might be in
// the ground in many cases // the ground in many cases
Vector3 offset = Vector3.up * 0.01f; Vector3 offset = Vector3.up * 0.01f;
// draw position // draw position
Gizmos.color = color; Gizmos.color = color;
Gizmos.DrawSphere(data.position + offset, 0.5f); Gizmos.DrawSphere(data.localPosition + offset, 0.5f);
// draw forward and up // draw forward and up
Gizmos.color = Color.blue; // like unity move tool Gizmos.color = Color.blue; // like unity move tool
Gizmos.DrawRay(data.position + offset, data.rotation * Vector3.forward); Gizmos.DrawRay(data.localPosition + offset, data.localRotation * Vector3.forward);
Gizmos.color = Color.green; // like unity move tool Gizmos.color = Color.green; // like unity move tool
Gizmos.DrawRay(data.position + offset, data.rotation * Vector3.up); Gizmos.DrawRay(data.localPosition + offset, data.localRotation * Vector3.up);
} }
static void DrawLineBetweenDataPoints(DataPoint data1, DataPoint data2, Color color) static void DrawLineBetweenDataPoints(DataPoint data1, DataPoint data2, Color color)
{ {
Gizmos.color = Color.white; Gizmos.color = Color.white;
Gizmos.DrawLine(data1.position, data2.position); Gizmos.DrawLine(data1.localPosition, data2.localPosition);
} }
// draw the data points for easier debugging // draw the data points for easier debugging