From 891c9889a2fbbf0ecb49d6dc229a52cfd6aced59 Mon Sep 17 00:00:00 2001 From: becksebenius-unity <74328025+becksebenius-unity@users.noreply.github.com> Date: Thu, 8 Apr 2021 01:48:10 -0700 Subject: [PATCH] feat: Add NetworkRigidbody extension (#35) * Added NetworkRigidbody component * Added readme * docs: Include NetworkRigidbody in readme Co-authored-by: Luke Stampfli <43687322+LukeStampfli@users.noreply.github.com> --- README.md | 1 + .../NetworkRigidbody/NetworkRigidbody.cs | 174 ++++++++++++++++++ .../Runtime/NetworkRigidbody/README.md | 18 ++ 3 files changed, 193 insertions(+) create mode 100644 com.mlapi.contrib.extensions/Runtime/NetworkRigidbody/NetworkRigidbody.cs create mode 100644 com.mlapi.contrib.extensions/Runtime/NetworkRigidbody/README.md diff --git a/README.md b/README.md index 8b7966a..61fc5d7 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Check our [contribution guidelines](CONTRIBUTING.md) for information on how to c |:------------:|:-------------:|:-------:|:---:| |**[NetworkObjectPool](/com.mlapi.contrib.extensions/Runtime/NetworkObjectPool)**| | :heavy_check_mark: | | |**[NetworkManagerHud](/com.mlapi.contrib.extensions/Runtime/NetworkManagerHud)**| | :heavy_check_mark: | | +|**[NetworkRigidBody](/com.mlapi.contrib.extensions/Runtime/NetworkRigidbody)**| | :heavy_check_mark: | | ### Releases diff --git a/com.mlapi.contrib.extensions/Runtime/NetworkRigidbody/NetworkRigidbody.cs b/com.mlapi.contrib.extensions/Runtime/NetworkRigidbody/NetworkRigidbody.cs new file mode 100644 index 0000000..4eae9ef --- /dev/null +++ b/com.mlapi.contrib.extensions/Runtime/NetworkRigidbody/NetworkRigidbody.cs @@ -0,0 +1,174 @@ +using System; +using MLAPI; +using MLAPI.NetworkVariable; +using UnityEngine; + +public class NetworkRigidbody : NetworkBehaviour +{ + public NetworkVariableVector3 netVelocity = new NetworkVariableVector3(NetworkVariableSettingsUtility.OwnerWrite); + public NetworkVariableVector3 netAngularVelocity = new NetworkVariableVector3(NetworkVariableSettingsUtility.OwnerWrite); + public NetworkVariableVector3 netPosition = new NetworkVariableVector3(NetworkVariableSettingsUtility.OwnerWrite); + public NetworkVariableQuaternion netRotation = new NetworkVariableQuaternion(NetworkVariableSettingsUtility.OwnerWrite); + public NetworkVariableUInt netUpdateId = new NetworkVariableUInt(NetworkVariableSettingsUtility.OwnerWrite); + + [SerializeField] + bool m_SyncVelocity = true; + + [SerializeField] + bool m_SyncAngularVelocity = true; + + [SerializeField] + bool m_SyncPosition = true; + + [SerializeField] + bool m_SyncRotation = true; + + [SerializeField] + float m_InterpolationTime; + + [Serializable] + struct InterpolationState + { + public Vector3 PositionDelta; + public Quaternion RotationDelta; + public Vector3 VelocityDelta; + public Vector3 AngularVelocityDelta; + public float TimeRemaining; + public float TotalTime; + } + + uint m_InterpolationChangeId; + InterpolationState m_InterpolationState; + Rigidbody m_Rigidbody; + + void Awake() + { + m_Rigidbody = GetComponent(); + } + + void BeginInterpolation() + { + m_InterpolationState = new InterpolationState() + { + PositionDelta = netPosition.Value - m_Rigidbody.position, + RotationDelta = Quaternion.Inverse(m_Rigidbody.rotation) * netRotation.Value, + VelocityDelta = netVelocity.Value - m_Rigidbody.velocity, + AngularVelocityDelta = netAngularVelocity.Value - m_Rigidbody.angularVelocity, + TimeRemaining = m_InterpolationTime, + TotalTime = m_InterpolationTime + }; + } + + void FixedUpdate() + { + if (!NetworkVariablesInitialized()) + { + return; + } + + if (IsOwner) + { + bool changed = false; + + if (m_SyncPosition) + { + changed |= TryUpdate(netPosition, m_Rigidbody.position); + } + + if (m_SyncRotation) + { + changed |= TryUpdate(netRotation, m_Rigidbody.rotation); + } + + if (m_SyncVelocity) + { + changed |= TryUpdate(netVelocity, m_Rigidbody.velocity); + } + + if (m_SyncAngularVelocity) + { + changed |= TryUpdate(netAngularVelocity, m_Rigidbody.angularVelocity); + } + + if (changed) + { + netUpdateId.Value++; + } + } + else + { + if (m_InterpolationChangeId != netUpdateId.Value) + { + BeginInterpolation(); + m_InterpolationChangeId = netUpdateId.Value; + } + + float deltaTime = Time.fixedDeltaTime; + if (0 < m_InterpolationState.TimeRemaining) + { + deltaTime = Mathf.Min(deltaTime, m_InterpolationState.TimeRemaining); + m_InterpolationState.TimeRemaining -= deltaTime; + + deltaTime /= m_InterpolationState.TotalTime; + + if (m_SyncPosition) + { + m_Rigidbody.position += + m_InterpolationState.PositionDelta * deltaTime; + } + + if (m_SyncRotation) + { + m_Rigidbody.rotation = + m_Rigidbody.rotation * Quaternion.Slerp(Quaternion.identity, m_InterpolationState.RotationDelta, deltaTime); + } + + if (m_SyncVelocity) + { + m_Rigidbody.velocity += + m_InterpolationState.VelocityDelta * deltaTime; + } + + if (m_SyncAngularVelocity) + { + m_Rigidbody.angularVelocity += + m_InterpolationState.AngularVelocityDelta * deltaTime; + } + } + } + } + + bool NetworkVariablesInitialized() + { + return netVelocity.Settings.WritePermission == NetworkVariablePermission.OwnerOnly; + } + + static bool TryUpdate(NetworkVariableVector3 variable, Vector3 value) + { + var current = variable.Value; + if (Mathf.Approximately(current.x, value.x) + && Mathf.Approximately(current.y, value.y) + && Mathf.Approximately(current.z, value.z)) + { + return false; + } + + variable.Value = value; + return true; + } + + static bool TryUpdate(NetworkVariableQuaternion variable, Quaternion value) + { + var current = variable.Value; + if (Mathf.Approximately(current.x, value.x) + && Mathf.Approximately(current.y, value.y) + && Mathf.Approximately(current.z, value.z) + && Mathf.Approximately(current.w, value.w)) + { + return false; + } + + variable.Value = value; + return true; + } +} \ No newline at end of file diff --git a/com.mlapi.contrib.extensions/Runtime/NetworkRigidbody/README.md b/com.mlapi.contrib.extensions/Runtime/NetworkRigidbody/README.md new file mode 100644 index 0000000..44870f9 --- /dev/null +++ b/com.mlapi.contrib.extensions/Runtime/NetworkRigidbody/README.md @@ -0,0 +1,18 @@ +# NetworkRigidbody + +Provides a component that replicates a rigidbody's properties over the network. Supports client authority via NetworkObject Ownership. You can also select which properties you want to sync. + +Synced properties: + +- `Rigidbody.position` +- `Rigidbody.rotation` +- `Rigidbody.velocity` +- `Rigidbody.angularVelocity` + +## Interpolation + +NetworkRigidbody is linearly interpolated. The interpolation time can be set as a serialized property on the component. Interpolation resets each time network variables are updated. + +## Prediction + +NetworkRigidbody does not suppress local physics. NetworkObjects not under ownership by the local client will continue to simulate so that movement appears smooth. Rubber banding will be most obvious in scenarios of volatile latency or mixed-ownership collisions. \ No newline at end of file