NetworkRigidbody.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. using UnityEngine;
  2. namespace Mirror.Experimental
  3. {
  4. [AddComponentMenu("Network/Experimental/NetworkRigidbody")]
  5. [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-rigidbody")]
  6. public class NetworkRigidbody : NetworkBehaviour
  7. {
  8. [Header("Settings")]
  9. [SerializeField] internal Rigidbody target = null;
  10. [Tooltip("Set to true if moves come from owner client, set to false if moves always come from server")]
  11. public bool clientAuthority = false;
  12. [Header("Velocity")]
  13. [Tooltip("Syncs Velocity every SyncInterval")]
  14. [SerializeField] bool syncVelocity = true;
  15. [Tooltip("Set velocity to 0 each frame (only works if syncVelocity is false")]
  16. [SerializeField] bool clearVelocity = false;
  17. [Tooltip("Only Syncs Value if distance between previous and current is great than sensitivity")]
  18. [SerializeField] float velocitySensitivity = 0.1f;
  19. [Header("Angular Velocity")]
  20. [Tooltip("Syncs AngularVelocity every SyncInterval")]
  21. [SerializeField] bool syncAngularVelocity = true;
  22. [Tooltip("Set angularVelocity to 0 each frame (only works if syncAngularVelocity is false")]
  23. [SerializeField] bool clearAngularVelocity = false;
  24. [Tooltip("Only Syncs Value if distance between previous and current is great than sensitivity")]
  25. [SerializeField] float angularVelocitySensitivity = 0.1f;
  26. /// <summary>
  27. /// Values sent on client with authority after they are sent to the server
  28. /// </summary>
  29. readonly ClientSyncState previousValue = new ClientSyncState();
  30. void OnValidate()
  31. {
  32. if (target == null)
  33. {
  34. target = GetComponent<Rigidbody>();
  35. }
  36. }
  37. #region Sync vars
  38. [SyncVar(hook = nameof(OnVelocityChanged))]
  39. Vector3 velocity;
  40. [SyncVar(hook = nameof(OnAngularVelocityChanged))]
  41. Vector3 angularVelocity;
  42. [SyncVar(hook = nameof(OnIsKinematicChanged))]
  43. bool isKinematic;
  44. [SyncVar(hook = nameof(OnUseGravityChanged))]
  45. bool useGravity;
  46. [SyncVar(hook = nameof(OnuDragChanged))]
  47. float drag;
  48. [SyncVar(hook = nameof(OnAngularDragChanged))]
  49. float angularDrag;
  50. /// <summary>
  51. /// Ignore value if is host or client with Authority
  52. /// </summary>
  53. /// <returns></returns>
  54. bool IgnoreSync => isServer || ClientWithAuthority;
  55. bool ClientWithAuthority => clientAuthority && hasAuthority;
  56. void OnVelocityChanged(Vector3 _, Vector3 newValue)
  57. {
  58. if (IgnoreSync)
  59. return;
  60. target.velocity = newValue;
  61. }
  62. void OnAngularVelocityChanged(Vector3 _, Vector3 newValue)
  63. {
  64. if (IgnoreSync)
  65. return;
  66. target.angularVelocity = newValue;
  67. }
  68. void OnIsKinematicChanged(bool _, bool newValue)
  69. {
  70. if (IgnoreSync)
  71. return;
  72. target.isKinematic = newValue;
  73. }
  74. void OnUseGravityChanged(bool _, bool newValue)
  75. {
  76. if (IgnoreSync)
  77. return;
  78. target.useGravity = newValue;
  79. }
  80. void OnuDragChanged(float _, float newValue)
  81. {
  82. if (IgnoreSync)
  83. return;
  84. target.drag = newValue;
  85. }
  86. void OnAngularDragChanged(float _, float newValue)
  87. {
  88. if (IgnoreSync)
  89. return;
  90. target.angularDrag = newValue;
  91. }
  92. #endregion
  93. internal void Update()
  94. {
  95. if (isServer)
  96. {
  97. SyncToClients();
  98. }
  99. else if (ClientWithAuthority)
  100. {
  101. SendToServer();
  102. }
  103. }
  104. internal void FixedUpdate()
  105. {
  106. if (clearAngularVelocity && !syncAngularVelocity)
  107. {
  108. target.angularVelocity = Vector3.zero;
  109. }
  110. if (clearVelocity && !syncVelocity)
  111. {
  112. target.velocity = Vector3.zero;
  113. }
  114. }
  115. /// <summary>
  116. /// Updates sync var values on server so that they sync to the client
  117. /// </summary>
  118. [Server]
  119. void SyncToClients()
  120. {
  121. // only update if they have changed more than Sensitivity
  122. Vector3 currentVelocity = syncVelocity ? target.velocity : default;
  123. Vector3 currentAngularVelocity = syncAngularVelocity ? target.angularVelocity : default;
  124. bool velocityChanged = syncVelocity && ((previousValue.velocity - currentVelocity).sqrMagnitude > velocitySensitivity * velocitySensitivity);
  125. bool angularVelocityChanged = syncAngularVelocity && ((previousValue.angularVelocity - currentAngularVelocity).sqrMagnitude > angularVelocitySensitivity * angularVelocitySensitivity);
  126. if (velocityChanged)
  127. {
  128. velocity = currentVelocity;
  129. previousValue.velocity = currentVelocity;
  130. }
  131. if (angularVelocityChanged)
  132. {
  133. angularVelocity = currentAngularVelocity;
  134. previousValue.angularVelocity = currentAngularVelocity;
  135. }
  136. // other rigidbody settings
  137. isKinematic = target.isKinematic;
  138. useGravity = target.useGravity;
  139. drag = target.drag;
  140. angularDrag = target.angularDrag;
  141. }
  142. /// <summary>
  143. /// Uses Command to send values to server
  144. /// </summary>
  145. [Client]
  146. void SendToServer()
  147. {
  148. if (!hasAuthority)
  149. {
  150. Debug.LogWarning("SendToServer called without authority");
  151. return;
  152. }
  153. SendVelocity();
  154. SendRigidBodySettings();
  155. }
  156. [Client]
  157. void SendVelocity()
  158. {
  159. float now = Time.time;
  160. if (now < previousValue.nextSyncTime)
  161. return;
  162. Vector3 currentVelocity = syncVelocity ? target.velocity : default;
  163. Vector3 currentAngularVelocity = syncAngularVelocity ? target.angularVelocity : default;
  164. bool velocityChanged = syncVelocity && ((previousValue.velocity - currentVelocity).sqrMagnitude > velocitySensitivity * velocitySensitivity);
  165. bool angularVelocityChanged = syncAngularVelocity && ((previousValue.angularVelocity - currentAngularVelocity).sqrMagnitude > angularVelocitySensitivity * angularVelocitySensitivity);
  166. // if angularVelocity has changed it is likely that velocity has also changed so just sync both values
  167. // however if only velocity has changed just send velocity
  168. if (angularVelocityChanged)
  169. {
  170. CmdSendVelocityAndAngular(currentVelocity, currentAngularVelocity);
  171. previousValue.velocity = currentVelocity;
  172. previousValue.angularVelocity = currentAngularVelocity;
  173. }
  174. else if (velocityChanged)
  175. {
  176. CmdSendVelocity(currentVelocity);
  177. previousValue.velocity = currentVelocity;
  178. }
  179. // only update syncTime if either has changed
  180. if (angularVelocityChanged || velocityChanged)
  181. {
  182. previousValue.nextSyncTime = now + syncInterval;
  183. }
  184. }
  185. [Client]
  186. void SendRigidBodySettings()
  187. {
  188. // These shouldn't change often so it is ok to send in their own Command
  189. if (previousValue.isKinematic != target.isKinematic)
  190. {
  191. CmdSendIsKinematic(target.isKinematic);
  192. previousValue.isKinematic = target.isKinematic;
  193. }
  194. if (previousValue.useGravity != target.useGravity)
  195. {
  196. CmdSendUseGravity(target.useGravity);
  197. previousValue.useGravity = target.useGravity;
  198. }
  199. if (previousValue.drag != target.drag)
  200. {
  201. CmdSendDrag(target.drag);
  202. previousValue.drag = target.drag;
  203. }
  204. if (previousValue.angularDrag != target.angularDrag)
  205. {
  206. CmdSendAngularDrag(target.angularDrag);
  207. previousValue.angularDrag = target.angularDrag;
  208. }
  209. }
  210. /// <summary>
  211. /// Called when only Velocity has changed on the client
  212. /// </summary>
  213. [Command]
  214. void CmdSendVelocity(Vector3 velocity)
  215. {
  216. // Ignore messages from client if not in client authority mode
  217. if (!clientAuthority)
  218. return;
  219. this.velocity = velocity;
  220. target.velocity = velocity;
  221. }
  222. /// <summary>
  223. /// Called when angularVelocity has changed on the client
  224. /// </summary>
  225. [Command]
  226. void CmdSendVelocityAndAngular(Vector3 velocity, Vector3 angularVelocity)
  227. {
  228. // Ignore messages from client if not in client authority mode
  229. if (!clientAuthority)
  230. return;
  231. if (syncVelocity)
  232. {
  233. this.velocity = velocity;
  234. target.velocity = velocity;
  235. }
  236. this.angularVelocity = angularVelocity;
  237. target.angularVelocity = angularVelocity;
  238. }
  239. [Command]
  240. void CmdSendIsKinematic(bool isKinematic)
  241. {
  242. // Ignore messages from client if not in client authority mode
  243. if (!clientAuthority)
  244. return;
  245. this.isKinematic = isKinematic;
  246. target.isKinematic = isKinematic;
  247. }
  248. [Command]
  249. void CmdSendUseGravity(bool useGravity)
  250. {
  251. // Ignore messages from client if not in client authority mode
  252. if (!clientAuthority)
  253. return;
  254. this.useGravity = useGravity;
  255. target.useGravity = useGravity;
  256. }
  257. [Command]
  258. void CmdSendDrag(float drag)
  259. {
  260. // Ignore messages from client if not in client authority mode
  261. if (!clientAuthority)
  262. return;
  263. this.drag = drag;
  264. target.drag = drag;
  265. }
  266. [Command]
  267. void CmdSendAngularDrag(float angularDrag)
  268. {
  269. // Ignore messages from client if not in client authority mode
  270. if (!clientAuthority)
  271. return;
  272. this.angularDrag = angularDrag;
  273. target.angularDrag = angularDrag;
  274. }
  275. /// <summary>
  276. /// holds previously synced values
  277. /// </summary>
  278. public class ClientSyncState
  279. {
  280. /// <summary>
  281. /// Next sync time that velocity will be synced, based on syncInterval.
  282. /// </summary>
  283. public float nextSyncTime;
  284. public Vector3 velocity;
  285. public Vector3 angularVelocity;
  286. public bool isKinematic;
  287. public bool useGravity;
  288. public float drag;
  289. public float angularDrag;
  290. }
  291. }
  292. }