Mirror/Unity-Technologies-networking/Runtime/NetworkTransform.cs

1595 lines
55 KiB
C#
Raw Normal View History

#if ENABLE_UNET
using System;
using UnityEngine;
namespace UnityEngine.Networking
{
[DisallowMultipleComponent]
[AddComponentMenu("Network/NetworkTransform")]
public class NetworkTransform : NetworkBehaviour
{
public enum TransformSyncMode
{
SyncNone = 0,
SyncTransform = 1,
SyncRigidbody2D = 2,
SyncRigidbody3D = 3,
SyncCharacterController = 4
}
public enum AxisSyncMode
{
None,
AxisX,
AxisY,
AxisZ,
AxisXY,
AxisXZ,
AxisYZ,
AxisXYZ
}
public enum CompressionSyncMode
{
None,
Low,
High
}
public delegate bool ClientMoveCallback3D(ref Vector3 position, ref Vector3 velocity, ref Quaternion rotation);
public delegate bool ClientMoveCallback2D(ref Vector2 position, ref Vector2 velocity, ref float rotation);
[SerializeField] TransformSyncMode m_TransformSyncMode = TransformSyncMode.SyncNone;
[SerializeField] float m_SendInterval = 0.1f;
[SerializeField] AxisSyncMode m_SyncRotationAxis = AxisSyncMode.AxisXYZ;
[SerializeField] CompressionSyncMode m_RotationSyncCompression = CompressionSyncMode.None;
[SerializeField] bool m_SyncSpin;
[SerializeField] float m_MovementTheshold = 0.001f;
[SerializeField] float m_VelocityThreshold = 0.0001f;
[SerializeField] float m_SnapThreshold = 5.0f;
[SerializeField] float m_InterpolateRotation = 1.0f;
[SerializeField] float m_InterpolateMovement = 1.0f;
[SerializeField] ClientMoveCallback3D m_ClientMoveCallback3D;
[SerializeField] ClientMoveCallback2D m_ClientMoveCallback2D;
Rigidbody m_RigidBody3D;
Rigidbody2D m_RigidBody2D;
CharacterController m_CharacterController;
bool m_Grounded = true;
// movement smoothing
Vector3 m_TargetSyncPosition;
Vector3 m_TargetSyncVelocity;
Vector3 m_FixedPosDiff;
Quaternion m_TargetSyncRotation3D;
Vector3 m_TargetSyncAngularVelocity3D;
float m_TargetSyncRotation2D;
float m_TargetSyncAngularVelocity2D;
float m_LastClientSyncTime; // last time client received a sync from server
float m_LastClientSendTime; // last time client send a sync to server
Vector3 m_PrevPosition;
Quaternion m_PrevRotation;
float m_PrevRotation2D;
float m_PrevVelocity;
const float k_LocalMovementThreshold = 0.00001f;
const float k_LocalRotationThreshold = 0.00001f;
const float k_LocalVelocityThreshold = 0.00001f;
const float k_MoveAheadRatio = 0.1f;
NetworkWriter m_LocalTransformWriter;
// settings
public TransformSyncMode transformSyncMode { get { return m_TransformSyncMode; } set { m_TransformSyncMode = value; } }
public float sendInterval { get { return m_SendInterval; } set { m_SendInterval = value; } }
public AxisSyncMode syncRotationAxis { get { return m_SyncRotationAxis; } set { m_SyncRotationAxis = value; } }
public CompressionSyncMode rotationSyncCompression { get { return m_RotationSyncCompression; } set { m_RotationSyncCompression = value; } }
public bool syncSpin { get { return m_SyncSpin; } set { m_SyncSpin = value; } }
public float movementTheshold { get { return m_MovementTheshold; } set { m_MovementTheshold = value; } }
public float velocityThreshold { get { return m_VelocityThreshold; } set { m_VelocityThreshold = value; } }
public float snapThreshold { get { return m_SnapThreshold; } set { m_SnapThreshold = value; } }
public float interpolateRotation { get { return m_InterpolateRotation; } set { m_InterpolateRotation = value; } }
public float interpolateMovement { get { return m_InterpolateMovement; } set { m_InterpolateMovement = value; } }
public ClientMoveCallback3D clientMoveCallback3D { get { return m_ClientMoveCallback3D; } set { m_ClientMoveCallback3D = value; } }
public ClientMoveCallback2D clientMoveCallback2D { get { return m_ClientMoveCallback2D; } set { m_ClientMoveCallback2D = value; } }
// runtime data
public CharacterController characterContoller { get { return m_CharacterController; } }
public Rigidbody rigidbody3D { get { return m_RigidBody3D; } }
#if !PLATFORM_WINRT
new
#endif
public Rigidbody2D rigidbody2D { get { return m_RigidBody2D; } }
public float lastSyncTime { get { return m_LastClientSyncTime; } }
public Vector3 targetSyncPosition { get { return m_TargetSyncPosition; } }
public Vector3 targetSyncVelocity { get { return m_TargetSyncVelocity; } }
public Quaternion targetSyncRotation3D { get { return m_TargetSyncRotation3D; } }
public float targetSyncRotation2D { get { return m_TargetSyncRotation2D; } }
public bool grounded { get { return m_Grounded; } set { m_Grounded = value; } }
void OnValidate()
{
if (m_TransformSyncMode < TransformSyncMode.SyncNone || m_TransformSyncMode > TransformSyncMode.SyncCharacterController)
{
m_TransformSyncMode = TransformSyncMode.SyncTransform;
}
if (m_SendInterval < 0)
{
m_SendInterval = 0;
}
if (m_SyncRotationAxis < AxisSyncMode.None || m_SyncRotationAxis > AxisSyncMode.AxisXYZ)
{
m_SyncRotationAxis = AxisSyncMode.None;
}
if (m_MovementTheshold < 0)
{
m_MovementTheshold = 0.00f;
}
if (m_VelocityThreshold < 0)
{
m_VelocityThreshold = 0.00f;
}
if (m_SnapThreshold < 0)
{
m_SnapThreshold = 0.01f;
}
if (m_InterpolateRotation < 0)
{
m_InterpolateRotation = 0.01f;
}
if (m_InterpolateMovement < 0)
{
m_InterpolateMovement = 0.01f;
}
}
void Awake()
{
m_RigidBody3D = GetComponent<Rigidbody>();
m_RigidBody2D = GetComponent<Rigidbody2D>();
m_CharacterController = GetComponent<CharacterController>();
m_PrevPosition = transform.position;
m_PrevRotation = transform.rotation;
m_PrevVelocity = 0;
// cache these to avoid per-frame allocations.
if (localPlayerAuthority)
{
m_LocalTransformWriter = new NetworkWriter();
}
}
public override void OnStartServer()
{
m_LastClientSyncTime = 0;
}
public override bool OnSerialize(NetworkWriter writer, bool initialState)
{
if (initialState)
{
// always write initial state, no dirty bits
}
else if (syncVarDirtyBits == 0)
{
writer.WritePackedUInt32(0);
return false;
}
else
{
// dirty bits
writer.WritePackedUInt32(1);
}
switch (transformSyncMode)
{
case TransformSyncMode.SyncNone:
{
return false;
}
case TransformSyncMode.SyncTransform:
{
SerializeModeTransform(writer);
break;
}
case TransformSyncMode.SyncRigidbody3D:
{
SerializeMode3D(writer);
break;
}
case TransformSyncMode.SyncRigidbody2D:
{
SerializeMode2D(writer);
break;
}
case TransformSyncMode.SyncCharacterController:
{
SerializeModeCharacterController(writer);
break;
}
}
return true;
}
void SerializeModeTransform(NetworkWriter writer)
{
// position
writer.Write(transform.position);
// no velocity
// rotation
if (m_SyncRotationAxis != AxisSyncMode.None)
{
SerializeRotation3D(writer, transform.rotation, syncRotationAxis, rotationSyncCompression);
}
// no spin
m_PrevPosition = transform.position;
m_PrevRotation = transform.rotation;
m_PrevVelocity = 0;
}
void VerifySerializeComponentExists()
{
bool throwError = false;
Type componentMissing = null;
switch (transformSyncMode)
{
case TransformSyncMode.SyncCharacterController:
if (!m_CharacterController && !(m_CharacterController = GetComponent<CharacterController>()))
{
throwError = true;
componentMissing = typeof(CharacterController);
}
break;
case TransformSyncMode.SyncRigidbody2D:
if (!m_RigidBody2D && !(m_RigidBody2D = GetComponent<Rigidbody2D>()))
{
throwError = true;
componentMissing = typeof(Rigidbody2D);
}
break;
case TransformSyncMode.SyncRigidbody3D:
if (!m_RigidBody3D && !(m_RigidBody3D = GetComponent<Rigidbody>()))
{
throwError = true;
componentMissing = typeof(Rigidbody);
}
break;
}
if (throwError && componentMissing != null)
{
throw new InvalidOperationException(string.Format("transformSyncMode set to {0} but no {1} component was found, did you call NetworkServer.Spawn on a prefab?", transformSyncMode, componentMissing.Name));
}
}
void SerializeMode3D(NetworkWriter writer)
{
VerifySerializeComponentExists();
if (isServer && m_LastClientSyncTime != 0)
{
// target position
writer.Write(m_TargetSyncPosition);
// target velocity
SerializeVelocity3D(writer, m_TargetSyncVelocity, CompressionSyncMode.None);
if (syncRotationAxis != AxisSyncMode.None)
{
// target rotation
SerializeRotation3D(writer, m_TargetSyncRotation3D, syncRotationAxis, rotationSyncCompression);
}
}
else
{
// current position
writer.Write(m_RigidBody3D.position);
// current velocity
SerializeVelocity3D(writer, m_RigidBody3D.velocity, CompressionSyncMode.None);
if (syncRotationAxis != AxisSyncMode.None)
{
// current rotation
SerializeRotation3D(writer, m_RigidBody3D.rotation, syncRotationAxis, rotationSyncCompression);
}
}
// spin
if (m_SyncSpin)
{
SerializeSpin3D(writer, m_RigidBody3D.angularVelocity, syncRotationAxis, rotationSyncCompression);
}
m_PrevPosition = m_RigidBody3D.position;
m_PrevRotation = transform.rotation;
m_PrevVelocity = m_RigidBody3D.velocity.sqrMagnitude;
}
void SerializeModeCharacterController(NetworkWriter writer)
{
VerifySerializeComponentExists();
if (isServer && m_LastClientSyncTime != 0)
{
// target position
writer.Write(m_TargetSyncPosition);
// no velocity
if (syncRotationAxis != AxisSyncMode.None)
{
// target rotation
SerializeRotation3D(writer, m_TargetSyncRotation3D, syncRotationAxis, rotationSyncCompression);
}
}
else
{
// current position
writer.Write(transform.position);
// no velocity
if (syncRotationAxis != AxisSyncMode.None)
{
// current rotation
SerializeRotation3D(writer, transform.rotation, syncRotationAxis, rotationSyncCompression);
}
}
// no spin
m_PrevPosition = transform.position;
m_PrevRotation = transform.rotation;
m_PrevVelocity = 0;
}
void SerializeMode2D(NetworkWriter writer)
{
VerifySerializeComponentExists();
if (isServer && m_LastClientSyncTime != 0)
{
// target position
writer.Write((Vector2)m_TargetSyncPosition);
// target velocity
SerializeVelocity2D(writer, m_TargetSyncVelocity, CompressionSyncMode.None);
// target rotation
if (syncRotationAxis != AxisSyncMode.None)
{
float orientation = m_TargetSyncRotation2D % 360;
if (orientation < 0) orientation += 360;
SerializeRotation2D(writer, orientation, rotationSyncCompression);
}
}
else
{
// current position
writer.Write(m_RigidBody2D.position);
// current velocity
SerializeVelocity2D(writer, m_RigidBody2D.velocity, CompressionSyncMode.None);
// current rotation
if (syncRotationAxis != AxisSyncMode.None)
{
float orientation = m_RigidBody2D.rotation % 360;
if (orientation < 0) orientation += 360;
SerializeRotation2D(writer, orientation, rotationSyncCompression);
}
}
// spin
if (m_SyncSpin)
{
SerializeSpin2D(writer, m_RigidBody2D.angularVelocity, rotationSyncCompression);
}
m_PrevPosition = m_RigidBody2D.position;
m_PrevRotation = transform.rotation;
m_PrevVelocity = m_RigidBody2D.velocity.sqrMagnitude;
}
public override void OnDeserialize(NetworkReader reader, bool initialState)
{
if (isServer && NetworkServer.localClientActive)
return;
if (!initialState)
{
if (reader.ReadPackedUInt32() == 0)
return;
}
switch (transformSyncMode)
{
case TransformSyncMode.SyncNone:
{
return;
}
case TransformSyncMode.SyncTransform:
{
UnserializeModeTransform(reader, initialState);
break;
}
case TransformSyncMode.SyncRigidbody3D:
{
UnserializeMode3D(reader, initialState);
break;
}
case TransformSyncMode.SyncRigidbody2D:
{
UnserializeMode2D(reader, initialState);
break;
}
case TransformSyncMode.SyncCharacterController:
{
UnserializeModeCharacterController(reader, initialState);
break;
}
}
m_LastClientSyncTime = Time.time;
}
void UnserializeModeTransform(NetworkReader reader, bool initialState)
{
if (hasAuthority)
{
// this component must read the data that the server wrote, even if it ignores it.
// otherwise the NetworkReader stream will still contain that data for the next component.
// position
reader.ReadVector3();
if (syncRotationAxis != AxisSyncMode.None)
{
UnserializeRotation3D(reader, syncRotationAxis, rotationSyncCompression);
}
return;
}
if (isServer && m_ClientMoveCallback3D != null)
{
var pos = reader.ReadVector3();
var vel = Vector3.zero;
var rot = Quaternion.identity;
if (syncRotationAxis != AxisSyncMode.None)
{
rot = UnserializeRotation3D(reader, syncRotationAxis, rotationSyncCompression);
}
if (m_ClientMoveCallback3D(ref pos, ref vel, ref rot))
{
transform.position = pos;
if (syncRotationAxis != AxisSyncMode.None)
{
transform.rotation = rot;
}
}
else
{
// rejected by callback
return;
}
}
else
{
// position
transform.position = reader.ReadVector3();
// no velocity
// rotation
if (syncRotationAxis != AxisSyncMode.None)
{
transform.rotation = UnserializeRotation3D(reader, syncRotationAxis, rotationSyncCompression);
}
// no spin
}
}
void UnserializeMode3D(NetworkReader reader, bool initialState)
{
if (hasAuthority)
{
// this component must read the data that the server wrote, even if it ignores it.
// otherwise the NetworkReader stream will still contain that data for the next component.
// position
reader.ReadVector3();
// velocity
reader.ReadVector3();
if (syncRotationAxis != AxisSyncMode.None)
{
UnserializeRotation3D(reader, syncRotationAxis, rotationSyncCompression);
}
if (syncSpin)
{
UnserializeSpin3D(reader, syncRotationAxis, rotationSyncCompression);
}
return;
}
if (isServer && m_ClientMoveCallback3D != null)
{
var pos = reader.ReadVector3();
var vel = reader.ReadVector3();
Quaternion rot = Quaternion.identity;
if (syncRotationAxis != AxisSyncMode.None)
{
rot = UnserializeRotation3D(reader, syncRotationAxis, rotationSyncCompression);
}
if (m_ClientMoveCallback3D(ref pos, ref vel, ref rot))
{
m_TargetSyncPosition = pos;
m_TargetSyncVelocity = vel;
if (syncRotationAxis != AxisSyncMode.None)
{
m_TargetSyncRotation3D = rot;
}
}
else
{
// rejected by callback
return;
}
}
else
{
// position
m_TargetSyncPosition = reader.ReadVector3();
// velocity
m_TargetSyncVelocity = reader.ReadVector3();
// rotation
if (syncRotationAxis != AxisSyncMode.None)
{
m_TargetSyncRotation3D = UnserializeRotation3D(reader, syncRotationAxis, rotationSyncCompression);
}
}
// spin
if (syncSpin)
{
m_TargetSyncAngularVelocity3D = UnserializeSpin3D(reader, syncRotationAxis, rotationSyncCompression);
}
if (m_RigidBody3D == null)
return;
if (isServer && !isClient)
{
// dedicated server needs to apply immediately, there is no interpolation
m_RigidBody3D.MovePosition(m_TargetSyncPosition);
m_RigidBody3D.MoveRotation(m_TargetSyncRotation3D);
m_RigidBody3D.velocity = m_TargetSyncVelocity;
return;
}
// handle zero send rate
if (GetNetworkSendInterval() == 0)
{
m_RigidBody3D.MovePosition(m_TargetSyncPosition);
m_RigidBody3D.velocity = m_TargetSyncVelocity;
if (syncRotationAxis != AxisSyncMode.None)
{
m_RigidBody3D.MoveRotation(m_TargetSyncRotation3D);
}
if (syncSpin)
{
m_RigidBody3D.angularVelocity = m_TargetSyncAngularVelocity3D;
}
return;
}
// handle position snap threshold
float dist = (m_RigidBody3D.position - m_TargetSyncPosition).magnitude;
if (dist > snapThreshold)
{
m_RigidBody3D.position = m_TargetSyncPosition;
m_RigidBody3D.velocity = m_TargetSyncVelocity;
}
// handle no rotation interpolation
if (interpolateRotation == 0 && syncRotationAxis != AxisSyncMode.None)
{
m_RigidBody3D.rotation = m_TargetSyncRotation3D;
if (syncSpin)
{
m_RigidBody3D.angularVelocity = m_TargetSyncAngularVelocity3D;
}
}
// handle no movement interpolation
if (m_InterpolateMovement == 0)
{
m_RigidBody3D.position = m_TargetSyncPosition;
}
if (initialState && syncRotationAxis != AxisSyncMode.None)
{
m_RigidBody3D.rotation = m_TargetSyncRotation3D;
}
}
void UnserializeMode2D(NetworkReader reader, bool initialState)
{
if (hasAuthority)
{
// this component must read the data that the server wrote, even if it ignores it.
// otherwise the NetworkReader stream will still contain that data for the next component.
// position
reader.ReadVector2();
// velocity
reader.ReadVector2();
if (syncRotationAxis != AxisSyncMode.None)
{
UnserializeRotation2D(reader, rotationSyncCompression);
}
if (syncSpin)
{
UnserializeSpin2D(reader, rotationSyncCompression);
}
return;
}
if (m_RigidBody2D == null)
return;
if (isServer && m_ClientMoveCallback2D != null)
{
Vector2 pos = reader.ReadVector2();
Vector2 vel = reader.ReadVector2();
float rot = 0;
if (syncRotationAxis != AxisSyncMode.None)
{
rot = UnserializeRotation2D(reader, rotationSyncCompression);
}
if (m_ClientMoveCallback2D(ref pos, ref vel, ref rot))
{
m_TargetSyncPosition = pos;
m_TargetSyncVelocity = vel;
if (syncRotationAxis != AxisSyncMode.None)
{
m_TargetSyncRotation2D = rot;
}
}
else
{
// rejected by callback
return;
}
}
else
{
// position
m_TargetSyncPosition = reader.ReadVector2();
// velocity
m_TargetSyncVelocity = reader.ReadVector2();
// rotation
if (syncRotationAxis != AxisSyncMode.None)
{
m_TargetSyncRotation2D = UnserializeRotation2D(reader, rotationSyncCompression);
}
}
// spin
if (syncSpin)
{
m_TargetSyncAngularVelocity2D = UnserializeSpin2D(reader, rotationSyncCompression);
}
if (isServer && !isClient)
{
// dedicated server needs to apply immediately, there is no interpolation
transform.position = m_TargetSyncPosition;
m_RigidBody2D.MoveRotation(m_TargetSyncRotation2D);
m_RigidBody2D.velocity = m_TargetSyncVelocity;
return;
}
// handle zero send rate
if (GetNetworkSendInterval() == 0)
{
// NOTE: cannot use m_RigidBody2D.MovePosition() and set velocity in the same frame, so use transform.position
transform.position = m_TargetSyncPosition;
m_RigidBody2D.velocity = m_TargetSyncVelocity;
if (syncRotationAxis != AxisSyncMode.None)
{
m_RigidBody2D.MoveRotation(m_TargetSyncRotation2D);
}
if (syncSpin)
{
m_RigidBody2D.angularVelocity = m_TargetSyncAngularVelocity2D;
}
return;
}
// handle position snap threshold
float dist = (m_RigidBody2D.position - (Vector2)m_TargetSyncPosition).magnitude;
if (dist > snapThreshold)
{
m_RigidBody2D.position = m_TargetSyncPosition;
m_RigidBody2D.velocity = m_TargetSyncVelocity;
}
// handle no rotation interpolation
if (interpolateRotation == 0 && syncRotationAxis != AxisSyncMode.None)
{
m_RigidBody2D.rotation = m_TargetSyncRotation2D;
if (syncSpin)
{
m_RigidBody2D.angularVelocity = m_TargetSyncAngularVelocity2D;
}
}
// handle no movement interpolation
if (m_InterpolateMovement == 0)
{
m_RigidBody2D.position = m_TargetSyncPosition;
}
if (initialState)
{
m_RigidBody2D.rotation = m_TargetSyncRotation2D;
}
}
void UnserializeModeCharacterController(NetworkReader reader, bool initialState)
{
if (hasAuthority)
{
// this component must read the data that the server wrote, even if it ignores it.
// otherwise the NetworkReader stream will still contain that data for the next component.
// position
reader.ReadVector3();
if (syncRotationAxis != AxisSyncMode.None)
{
UnserializeRotation3D(reader, syncRotationAxis, rotationSyncCompression);
}
return;
}
if (isServer && m_ClientMoveCallback3D != null)
{
var pos = reader.ReadVector3();
Quaternion rot = Quaternion.identity;
if (syncRotationAxis != AxisSyncMode.None)
{
rot = UnserializeRotation3D(reader, syncRotationAxis, rotationSyncCompression);
}
if (m_CharacterController == null)
return;
// no velocity in packet, use current local velocity
var vel = m_CharacterController.velocity;
if (m_ClientMoveCallback3D(ref pos, ref vel, ref rot))
{
m_TargetSyncPosition = pos;
m_TargetSyncVelocity = vel;
if (syncRotationAxis != AxisSyncMode.None)
{
m_TargetSyncRotation3D = rot;
}
}
else
{
// rejected by callback
return;
}
}
else
{
// position
m_TargetSyncPosition = reader.ReadVector3();
// no velocity
// rotation
if (syncRotationAxis != AxisSyncMode.None)
{
m_TargetSyncRotation3D = UnserializeRotation3D(reader, syncRotationAxis, rotationSyncCompression);
}
// no spin
}
if (m_CharacterController == null)
return;
// total distance away the target position is
var totalDistToTarget = (m_TargetSyncPosition - transform.position); // 5 units
var perSecondDist = totalDistToTarget / GetNetworkSendInterval();
m_FixedPosDiff = perSecondDist * Time.fixedDeltaTime;
if (isServer && !isClient)
{
// dedicated server needs to apply immediately, there is no interpolation
transform.position = m_TargetSyncPosition;
transform.rotation = m_TargetSyncRotation3D;
return;
}
// handle zero send rate
if (GetNetworkSendInterval() == 0)
{
transform.position = m_TargetSyncPosition;
//m_RigidBody3D.velocity = m_TargetSyncVelocity;
if (syncRotationAxis != AxisSyncMode.None)
{
transform.rotation = m_TargetSyncRotation3D;
}
return;
}
// handle position snap threshold
float dist = (transform.position - m_TargetSyncPosition).magnitude;
if (dist > snapThreshold)
{
transform.position = m_TargetSyncPosition;
}
// handle no rotation interpolation
if (interpolateRotation == 0 && syncRotationAxis != AxisSyncMode.None)
{
transform.rotation = m_TargetSyncRotation3D;
}
// handle no movement interpolation
if (m_InterpolateMovement == 0)
{
transform.position = m_TargetSyncPosition;
}
if (initialState && syncRotationAxis != AxisSyncMode.None)
{
transform.rotation = m_TargetSyncRotation3D;
}
}
void FixedUpdate()
{
if (isServer)
{
FixedUpdateServer();
}
if (isClient)
{
FixedUpdateClient();
}
}
void FixedUpdateServer()
{
if (syncVarDirtyBits != 0)
return;
// dont run if network isn't active
if (!NetworkServer.active)
return;
// dont run if we haven't been spawned yet
if (!isServer)
return;
// dont' auto-dirty if no send interval
if (GetNetworkSendInterval() == 0)
return;
float distance = (transform.position - m_PrevPosition).magnitude;
if (distance < movementTheshold)
{
distance = Quaternion.Angle(m_PrevRotation, transform.rotation);
if (distance < movementTheshold)
{
if (!CheckVelocityChanged())
{
return;
}
}
}
// This will cause transform to be sent
SetDirtyBit(1);
}
bool CheckVelocityChanged()
{
switch (transformSyncMode)
{
case TransformSyncMode.SyncRigidbody2D:
if (m_RigidBody2D && m_VelocityThreshold > 0)
{
return Mathf.Abs(m_RigidBody2D.velocity.sqrMagnitude - m_PrevVelocity) >= m_VelocityThreshold;
}
else
{
return false;
}
case TransformSyncMode.SyncRigidbody3D:
if (m_RigidBody3D && m_VelocityThreshold > 0)
{
return Mathf.Abs(m_RigidBody3D.velocity.sqrMagnitude - m_PrevVelocity) >= m_VelocityThreshold;
}
else
{
return false;
}
default:
return false;
}
}
void FixedUpdateClient()
{
// dont run if we haven't received any sync data
if (m_LastClientSyncTime == 0)
return;
// dont run if network isn't active
if (!NetworkServer.active && !NetworkClient.active)
return;
// dont run if we haven't been spawned yet
if (!isServer && !isClient)
return;
// dont run if not expecting continuous updates
if (GetNetworkSendInterval() == 0)
return;
// dont run this if this client has authority over this player object
if (hasAuthority)
return;
// interpolate on client
switch (transformSyncMode)
{
case TransformSyncMode.SyncNone:
{
return;
}
case TransformSyncMode.SyncTransform:
{
return;
}
case TransformSyncMode.SyncRigidbody3D:
{
InterpolateTransformMode3D();
break;
}
case TransformSyncMode.SyncRigidbody2D:
{
InterpolateTransformMode2D();
break;
}
case TransformSyncMode.SyncCharacterController:
{
InterpolateTransformModeCharacterController();
break;
}
}
}
void InterpolateTransformMode3D()
{
if (m_InterpolateMovement != 0)
{
Vector3 newVelocity = (m_TargetSyncPosition - m_RigidBody3D.position) * m_InterpolateMovement / GetNetworkSendInterval();
m_RigidBody3D.velocity = newVelocity;
}
if (interpolateRotation != 0)
{
m_RigidBody3D.MoveRotation(Quaternion.Slerp(
m_RigidBody3D.rotation,
m_TargetSyncRotation3D,
Time.fixedDeltaTime * interpolateRotation));
//m_TargetSyncRotation3D *= Quaternion.Euler(m_TargetSyncAngularVelocity3D * Time.fixedDeltaTime);
// move sync rotation slightly in rotation direction
//m_TargetSyncRotation3D += (m_TargetSyncAngularVelocity3D * Time.fixedDeltaTime * moveAheadRatio);
}
// move sync position slightly in the position of velocity
m_TargetSyncPosition += (m_TargetSyncVelocity * Time.fixedDeltaTime * k_MoveAheadRatio);
}
void InterpolateTransformModeCharacterController()
{
if (m_FixedPosDiff == Vector3.zero && m_TargetSyncRotation3D == transform.rotation)
return;
if (m_InterpolateMovement != 0)
{
m_CharacterController.Move(m_FixedPosDiff * m_InterpolateMovement);
}
if (interpolateRotation != 0)
{
transform.rotation = Quaternion.Slerp(
transform.rotation,
m_TargetSyncRotation3D,
Time.fixedDeltaTime * interpolateRotation * 10);
}
if (Time.time - m_LastClientSyncTime > GetNetworkSendInterval())
{
// turn off interpolation if we go out of the time window for a new packet
m_FixedPosDiff = Vector3.zero;
var diff = m_TargetSyncPosition - transform.position;
m_CharacterController.Move(diff);
}
}
void InterpolateTransformMode2D()
{
if (m_InterpolateMovement != 0)
{
Vector2 oldVelocity = m_RigidBody2D.velocity;
Vector2 newVelocity = (((Vector2)m_TargetSyncPosition - m_RigidBody2D.position)) * m_InterpolateMovement / GetNetworkSendInterval();
if (!m_Grounded && newVelocity.y < 0)
{
newVelocity.y = oldVelocity.y;
}
m_RigidBody2D.velocity = newVelocity;
}
if (interpolateRotation != 0)
{
float orientation = m_RigidBody2D.rotation % 360;
if (orientation < 0)
{
orientation += 360;
}
Quaternion newRotation = Quaternion.Slerp(
transform.rotation,
Quaternion.Euler(0, 0, m_TargetSyncRotation2D),
Time.fixedDeltaTime * interpolateRotation / GetNetworkSendInterval());
m_RigidBody2D.MoveRotation(newRotation.eulerAngles.z);
// move sync rotation slightly in rotation direction
m_TargetSyncRotation2D += (m_TargetSyncAngularVelocity2D * Time.fixedDeltaTime * k_MoveAheadRatio);
}
// move sync position slightly in the position of velocity
m_TargetSyncPosition += (m_TargetSyncVelocity * Time.fixedDeltaTime * k_MoveAheadRatio);
}
// --------------------- local transform sync ------------------------
void Update()
{
if (!hasAuthority)
return;
if (!localPlayerAuthority)
return;
if (NetworkServer.active)
return;
if (Time.time - m_LastClientSendTime > GetNetworkSendInterval())
{
SendTransform();
m_LastClientSendTime = Time.time;
}
}
bool HasMoved()
{
float diff = 0;
// check if position has changed
if (m_RigidBody3D != null)
{
diff = (m_RigidBody3D.position - m_PrevPosition).magnitude;
}
else if (m_RigidBody2D != null)
{
diff = (m_RigidBody2D.position - (Vector2)m_PrevPosition).magnitude;
}
else
{
diff = (transform.position - m_PrevPosition).magnitude;
}
if (diff > k_LocalMovementThreshold)
{
return true;
}
// check if rotation has changed
if (m_RigidBody3D != null)
{
diff = Quaternion.Angle(m_RigidBody3D.rotation, m_PrevRotation);
}
else if (m_RigidBody2D != null)
{
diff = Math.Abs(m_RigidBody2D.rotation - m_PrevRotation2D);
}
else
{
diff = Quaternion.Angle(transform.rotation, m_PrevRotation);
}
if (diff > k_LocalRotationThreshold)
{
return true;
}
// check if velocty has changed
if (m_RigidBody3D != null)
{
diff = Mathf.Abs(m_RigidBody3D.velocity.sqrMagnitude - m_PrevVelocity);
}
else if (m_RigidBody2D != null)
{
diff = Mathf.Abs(m_RigidBody2D.velocity.sqrMagnitude - m_PrevVelocity);
}
if (diff > k_LocalVelocityThreshold)
{
return true;
}
return false;
}
[Client]
void SendTransform()
{
if (!HasMoved() || ClientScene.readyConnection == null)
{
return;
}
m_LocalTransformWriter.StartMessage(MsgType.LocalPlayerTransform);
m_LocalTransformWriter.Write(netId);
switch (transformSyncMode)
{
case TransformSyncMode.SyncNone:
{
return;
}
case TransformSyncMode.SyncTransform:
{
SerializeModeTransform(m_LocalTransformWriter);
break;
}
case TransformSyncMode.SyncRigidbody3D:
{
SerializeMode3D(m_LocalTransformWriter);
break;
}
case TransformSyncMode.SyncRigidbody2D:
{
SerializeMode2D(m_LocalTransformWriter);
break;
}
case TransformSyncMode.SyncCharacterController:
{
SerializeModeCharacterController(m_LocalTransformWriter);
break;
}
}
if (m_RigidBody3D != null)
{
m_PrevPosition = m_RigidBody3D.position;
m_PrevRotation = m_RigidBody3D.rotation;
m_PrevVelocity = m_RigidBody3D.velocity.sqrMagnitude;
}
else if (m_RigidBody2D != null)
{
m_PrevPosition = m_RigidBody2D.position;
m_PrevRotation2D = m_RigidBody2D.rotation;
m_PrevVelocity = m_RigidBody2D.velocity.sqrMagnitude;
}
else
{
m_PrevPosition = transform.position;
m_PrevRotation = transform.rotation;
}
m_LocalTransformWriter.FinishMessage();
#if UNITY_EDITOR
UnityEditor.NetworkDetailStats.IncrementStat(
UnityEditor.NetworkDetailStats.NetworkDirection.Outgoing,
MsgType.LocalPlayerTransform, "6:LocalPlayerTransform", 1);
#endif
ClientScene.readyConnection.SendWriter(m_LocalTransformWriter, GetNetworkChannel());
}
static public void HandleTransform(NetworkMessage netMsg)
{
NetworkInstanceId netId = netMsg.reader.ReadNetworkId();
#if UNITY_EDITOR
UnityEditor.NetworkDetailStats.IncrementStat(
UnityEditor.NetworkDetailStats.NetworkDirection.Incoming,
MsgType.LocalPlayerTransform, "6:LocalPlayerTransform", 1);
#endif
GameObject foundObj = NetworkServer.FindLocalObject(netId);
if (foundObj == null)
{
if (LogFilter.logError) { Debug.LogError("Received NetworkTransform data for GameObject that doesn't exist"); }
return;
}
NetworkTransform foundSync = foundObj.GetComponent<NetworkTransform>();
if (foundSync == null)
{
if (LogFilter.logError) { Debug.LogError("HandleTransform null target"); }
return;
}
if (!foundSync.localPlayerAuthority)
{
if (LogFilter.logError) { Debug.LogError("HandleTransform no localPlayerAuthority"); }
return;
}
if (netMsg.conn.clientOwnedObjects == null)
{
if (LogFilter.logError) { Debug.LogError("HandleTransform object not owned by connection"); }
return;
}
if (netMsg.conn.clientOwnedObjects.Contains(netId))
{
switch (foundSync.transformSyncMode)
{
case TransformSyncMode.SyncNone:
{
return;
}
case TransformSyncMode.SyncTransform:
{
foundSync.UnserializeModeTransform(netMsg.reader, false);
break;
}
case TransformSyncMode.SyncRigidbody3D:
{
foundSync.UnserializeMode3D(netMsg.reader, false);
break;
}
case TransformSyncMode.SyncRigidbody2D:
{
foundSync.UnserializeMode2D(netMsg.reader, false);
break;
}
case TransformSyncMode.SyncCharacterController:
{
foundSync.UnserializeModeCharacterController(netMsg.reader, false);
break;
}
}
foundSync.m_LastClientSyncTime = Time.time;
return;
}
if (LogFilter.logWarn) { Debug.LogWarning("HandleTransform netId:" + netId + " is not for a valid player"); }
}
// --------------------- Compression Helper functions ------------------------
static void WriteAngle(NetworkWriter writer, float angle, CompressionSyncMode compression)
{
switch (compression)
{
case CompressionSyncMode.None:
{
writer.Write(angle);
break;
}
case CompressionSyncMode.Low:
{
writer.Write((short)angle);
break;
}
case CompressionSyncMode.High:
{
writer.Write((short)angle);
break;
}
}
}
static float ReadAngle(NetworkReader reader, CompressionSyncMode compression)
{
switch (compression)
{
case CompressionSyncMode.None:
{
return reader.ReadSingle();
}
case CompressionSyncMode.Low:
{
return reader.ReadInt16();
}
case CompressionSyncMode.High:
{
return reader.ReadInt16();
}
}
return 0;
}
// --------------------- Serialization Helper functions ------------------------
static public void SerializeVelocity3D(NetworkWriter writer, Vector3 velocity, CompressionSyncMode compression)
{
writer.Write(velocity);
}
static public void SerializeVelocity2D(NetworkWriter writer, Vector2 velocity, CompressionSyncMode compression)
{
writer.Write(velocity);
}
static public void SerializeRotation3D(NetworkWriter writer, Quaternion rot, AxisSyncMode mode, CompressionSyncMode compression)
{
switch (mode)
{
case AxisSyncMode.None:
break;
case AxisSyncMode.AxisX:
WriteAngle(writer, rot.eulerAngles.x, compression);
break;
case AxisSyncMode.AxisY:
WriteAngle(writer, rot.eulerAngles.y, compression);
break;
case AxisSyncMode.AxisZ:
WriteAngle(writer, rot.eulerAngles.z, compression);
break;
case AxisSyncMode.AxisXY:
WriteAngle(writer, rot.eulerAngles.x, compression);
WriteAngle(writer, rot.eulerAngles.y, compression);
break;
case AxisSyncMode.AxisXZ:
WriteAngle(writer, rot.eulerAngles.x, compression);
WriteAngle(writer, rot.eulerAngles.z, compression);
break;
case AxisSyncMode.AxisYZ:
WriteAngle(writer, rot.eulerAngles.y, compression);
WriteAngle(writer, rot.eulerAngles.z, compression);
break;
case AxisSyncMode.AxisXYZ:
WriteAngle(writer, rot.eulerAngles.x, compression);
WriteAngle(writer, rot.eulerAngles.y, compression);
WriteAngle(writer, rot.eulerAngles.z, compression);
break;
}
}
static public void SerializeRotation2D(NetworkWriter writer, float rot, CompressionSyncMode compression)
{
WriteAngle(writer, rot, compression);
}
static public void SerializeSpin3D(NetworkWriter writer, Vector3 angularVelocity, AxisSyncMode mode, CompressionSyncMode compression)
{
switch (mode)
{
case AxisSyncMode.None:
break;
case AxisSyncMode.AxisX:
WriteAngle(writer, angularVelocity.x, compression);
break;
case AxisSyncMode.AxisY:
WriteAngle(writer, angularVelocity.y, compression);
break;
case AxisSyncMode.AxisZ:
WriteAngle(writer, angularVelocity.z, compression);
break;
case AxisSyncMode.AxisXY:
WriteAngle(writer, angularVelocity.x, compression);
WriteAngle(writer, angularVelocity.y, compression);
break;
case AxisSyncMode.AxisXZ:
WriteAngle(writer, angularVelocity.x, compression);
WriteAngle(writer, angularVelocity.z, compression);
break;
case AxisSyncMode.AxisYZ:
WriteAngle(writer, angularVelocity.y, compression);
WriteAngle(writer, angularVelocity.z, compression);
break;
case AxisSyncMode.AxisXYZ:
WriteAngle(writer, angularVelocity.x, compression);
WriteAngle(writer, angularVelocity.y, compression);
WriteAngle(writer, angularVelocity.z, compression);
break;
}
}
static public void SerializeSpin2D(NetworkWriter writer, float angularVelocity, CompressionSyncMode compression)
{
WriteAngle(writer, angularVelocity, compression);
}
static public Vector3 UnserializeVelocity3D(NetworkReader reader, CompressionSyncMode compression)
{
return reader.ReadVector3();
}
static public Vector3 UnserializeVelocity2D(NetworkReader reader, CompressionSyncMode compression)
{
return reader.ReadVector2();
}
static public Quaternion UnserializeRotation3D(NetworkReader reader, AxisSyncMode mode, CompressionSyncMode compression)
{
Quaternion rotation = Quaternion.identity;
Vector3 rotv = Vector3.zero;
switch (mode)
{
case AxisSyncMode.None:
break;
case AxisSyncMode.AxisX:
rotv.Set(ReadAngle(reader, compression), 0, 0);
rotation.eulerAngles = rotv;
break;
case AxisSyncMode.AxisY:
rotv.Set(0, ReadAngle(reader, compression), 0);
rotation.eulerAngles = rotv;
break;
case AxisSyncMode.AxisZ:
rotv.Set(0, 0, ReadAngle(reader, compression));
rotation.eulerAngles = rotv;
break;
case AxisSyncMode.AxisXY:
rotv.Set(ReadAngle(reader, compression), ReadAngle(reader, compression), 0);
rotation.eulerAngles = rotv;
break;
case AxisSyncMode.AxisXZ:
rotv.Set(ReadAngle(reader, compression), 0, ReadAngle(reader, compression));
rotation.eulerAngles = rotv;
break;
case AxisSyncMode.AxisYZ:
rotv.Set(0, ReadAngle(reader, compression), ReadAngle(reader, compression));
rotation.eulerAngles = rotv;
break;
case AxisSyncMode.AxisXYZ:
rotv.Set(ReadAngle(reader, compression), ReadAngle(reader, compression), ReadAngle(reader, compression));
rotation.eulerAngles = rotv;
break;
}
return rotation;
}
static public float UnserializeRotation2D(NetworkReader reader, CompressionSyncMode compression)
{
return ReadAngle(reader, compression);
}
static public Vector3 UnserializeSpin3D(NetworkReader reader, AxisSyncMode mode, CompressionSyncMode compression)
{
Vector3 spin = Vector3.zero;
switch (mode)
{
case AxisSyncMode.None:
break;
case AxisSyncMode.AxisX:
spin.Set(ReadAngle(reader, compression), 0, 0);
break;
case AxisSyncMode.AxisY:
spin.Set(0, ReadAngle(reader, compression), 0);
break;
case AxisSyncMode.AxisZ:
spin.Set(0, 0, ReadAngle(reader, compression));
break;
case AxisSyncMode.AxisXY:
spin.Set(ReadAngle(reader, compression), ReadAngle(reader, compression), 0);
break;
case AxisSyncMode.AxisXZ:
spin.Set(ReadAngle(reader, compression), 0, ReadAngle(reader, compression));
break;
case AxisSyncMode.AxisYZ:
spin.Set(0, ReadAngle(reader, compression), ReadAngle(reader, compression));
break;
case AxisSyncMode.AxisXYZ:
spin.Set(ReadAngle(reader, compression), ReadAngle(reader, compression), ReadAngle(reader, compression));
break;
}
return spin;
}
static public float UnserializeSpin2D(NetworkReader reader, CompressionSyncMode compression)
{
return ReadAngle(reader, compression);
}
public override int GetNetworkChannel()
{
return Channels.DefaultUnreliable;
}
public override float GetNetworkSendInterval()
{
return m_SendInterval;
}
public override void OnStartAuthority()
{
// must reset this timer, or the server will continue to send target position instead of current position
m_LastClientSyncTime = 0;
}
}
}
#endif //ENABLE_UNET