using UnityEngine; namespace Mirror.Experimental { [AddComponentMenu("Network/ Experimental/Network Rigidbody 2D")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-rigidbody")] public class NetworkRigidbody2D : NetworkBehaviour { [Header("Settings")] [SerializeField] internal Rigidbody2D target = null; [Tooltip("Set to true if moves come from owner client, set to false if moves always come from server")] public bool clientAuthority = false; [Header("Velocity")] [Tooltip("Syncs Velocity every SyncInterval")] [SerializeField] bool syncVelocity = true; [Tooltip("Set velocity to 0 each frame (only works if syncVelocity is false")] [SerializeField] bool clearVelocity = false; [Tooltip("Only Syncs Value if distance between previous and current is great than sensitivity")] [SerializeField] float velocitySensitivity = 0.1f; [Header("Angular Velocity")] [Tooltip("Syncs AngularVelocity every SyncInterval")] [SerializeField] bool syncAngularVelocity = true; [Tooltip("Set angularVelocity to 0 each frame (only works if syncAngularVelocity is false")] [SerializeField] bool clearAngularVelocity = false; [Tooltip("Only Syncs Value if distance between previous and current is great than sensitivity")] [SerializeField] float angularVelocitySensitivity = 0.1f; /// /// Values sent on client with authority after they are sent to the server /// readonly ClientSyncState previousValue = new ClientSyncState(); protected override void OnValidate() { base.OnValidate(); Reset(); } public virtual void Reset() { if (target == null) target = GetComponent(); syncDirection = SyncDirection.ClientToServer; } #region Sync vars [SyncVar(hook = nameof(OnVelocityChanged))] Vector2 velocity; [SyncVar(hook = nameof(OnAngularVelocityChanged))] float angularVelocity; [SyncVar(hook = nameof(OnIsKinematicChanged))] bool isKinematic; [SyncVar(hook = nameof(OnGravityScaleChanged))] float gravityScale; [SyncVar(hook = nameof(OnuDragChanged))] float drag; [SyncVar(hook = nameof(OnAngularDragChanged))] float angularDrag; /// /// Ignore value if is host or client with Authority /// bool IgnoreSync => isServer || ClientWithAuthority; bool ClientWithAuthority => clientAuthority && isOwned; void OnVelocityChanged(Vector2 _, Vector2 newValue) { if (IgnoreSync) return; target.velocity = newValue; } void OnAngularVelocityChanged(float _, float newValue) { if (IgnoreSync) return; target.angularVelocity = newValue; } void OnIsKinematicChanged(bool _, bool newValue) { if (IgnoreSync) return; target.isKinematic = newValue; } void OnGravityScaleChanged(float _, float newValue) { if (IgnoreSync) return; target.gravityScale = newValue; } void OnuDragChanged(float _, float newValue) { if (IgnoreSync) return; target.drag = newValue; } void OnAngularDragChanged(float _, float newValue) { if (IgnoreSync) return; target.angularDrag = newValue; } #endregion internal void Update() { if (isServer) SyncToClients(); else if (ClientWithAuthority) SendToServer(); } internal void FixedUpdate() { if (clearAngularVelocity && !syncAngularVelocity) target.angularVelocity = 0f; if (clearVelocity && !syncVelocity) target.velocity = Vector2.zero; } /// /// Updates sync var values on server so that they sync to the client /// [Server] void SyncToClients() { // only update if they have changed more than Sensitivity Vector2 currentVelocity = syncVelocity ? target.velocity : default; float currentAngularVelocity = syncAngularVelocity ? target.angularVelocity : default; bool velocityChanged = syncVelocity && ((previousValue.velocity - currentVelocity).sqrMagnitude > velocitySensitivity * velocitySensitivity); bool angularVelocityChanged = syncAngularVelocity && ((previousValue.angularVelocity - currentAngularVelocity) > angularVelocitySensitivity); if (velocityChanged) { velocity = currentVelocity; previousValue.velocity = currentVelocity; } if (angularVelocityChanged) { angularVelocity = currentAngularVelocity; previousValue.angularVelocity = currentAngularVelocity; } // other rigidbody settings isKinematic = target.isKinematic; gravityScale = target.gravityScale; drag = target.drag; angularDrag = target.angularDrag; } /// /// Uses Command to send values to server /// [Client] void SendToServer() { if (!isOwned) { Debug.LogWarning("SendToServer called without authority"); return; } SendVelocity(); SendRigidBodySettings(); } [Client] void SendVelocity() { float now = Time.time; if (now < previousValue.nextSyncTime) return; Vector2 currentVelocity = syncVelocity ? target.velocity : default; float currentAngularVelocity = syncAngularVelocity ? target.angularVelocity : default; bool velocityChanged = syncVelocity && ((previousValue.velocity - currentVelocity).sqrMagnitude > velocitySensitivity * velocitySensitivity); bool angularVelocityChanged = syncAngularVelocity && previousValue.angularVelocity != currentAngularVelocity;//((previousValue.angularVelocity - currentAngularVelocity).sqrMagnitude > angularVelocitySensitivity * angularVelocitySensitivity); // if angularVelocity has changed it is likely that velocity has also changed so just sync both values // however if only velocity has changed just send velocity if (angularVelocityChanged) { CmdSendVelocityAndAngular(currentVelocity, currentAngularVelocity); previousValue.velocity = currentVelocity; previousValue.angularVelocity = currentAngularVelocity; } else if (velocityChanged) { CmdSendVelocity(currentVelocity); previousValue.velocity = currentVelocity; } // only update syncTime if either has changed if (angularVelocityChanged || velocityChanged) previousValue.nextSyncTime = now + syncInterval; } [Client] void SendRigidBodySettings() { // These shouldn't change often so it is ok to send in their own Command if (previousValue.isKinematic != target.isKinematic) { CmdSendIsKinematic(target.isKinematic); previousValue.isKinematic = target.isKinematic; } if (previousValue.gravityScale != target.gravityScale) { CmdChangeGravityScale(target.gravityScale); previousValue.gravityScale = target.gravityScale; } if (previousValue.drag != target.drag) { CmdSendDrag(target.drag); previousValue.drag = target.drag; } if (previousValue.angularDrag != target.angularDrag) { CmdSendAngularDrag(target.angularDrag); previousValue.angularDrag = target.angularDrag; } } /// /// Called when only Velocity has changed on the client /// [Command] void CmdSendVelocity(Vector2 velocity) { // Ignore messages from client if not in client authority mode if (!clientAuthority) return; this.velocity = velocity; target.velocity = velocity; } /// /// Called when angularVelocity has changed on the client /// [Command] void CmdSendVelocityAndAngular(Vector2 velocity, float angularVelocity) { // Ignore messages from client if not in client authority mode if (!clientAuthority) return; if (syncVelocity) { this.velocity = velocity; target.velocity = velocity; } this.angularVelocity = angularVelocity; target.angularVelocity = angularVelocity; } [Command] void CmdSendIsKinematic(bool isKinematic) { // Ignore messages from client if not in client authority mode if (!clientAuthority) return; this.isKinematic = isKinematic; target.isKinematic = isKinematic; } [Command] void CmdChangeGravityScale(float gravityScale) { // Ignore messages from client if not in client authority mode if (!clientAuthority) return; this.gravityScale = gravityScale; target.gravityScale = gravityScale; } [Command] void CmdSendDrag(float drag) { // Ignore messages from client if not in client authority mode if (!clientAuthority) return; this.drag = drag; target.drag = drag; } [Command] void CmdSendAngularDrag(float angularDrag) { // Ignore messages from client if not in client authority mode if (!clientAuthority) return; this.angularDrag = angularDrag; target.angularDrag = angularDrag; } /// /// holds previously synced values /// public class ClientSyncState { /// /// Next sync time that velocity will be synced, based on syncInterval. /// public float nextSyncTime; public Vector2 velocity; public float angularVelocity; public bool isKinematic; public float gravityScale; public float drag; public float angularDrag; } } }