NetworkRigidbody2D.cs 11 KB

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