mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 11:00:32 +00:00
NetworkTransform: use local position and rotation for VR support. Fixes #779
This commit is contained in:
parent
cb3d3dba72
commit
36be83d6fd
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user