PlayerController.cs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. using UnityEngine;
  2. namespace Mirror.Examples.TanksCoop
  3. {
  4. [RequireComponent(typeof(CapsuleCollider))]
  5. [RequireComponent(typeof(CharacterController))]
  6. [RequireComponent(typeof(NetworkTransformUnreliable))]
  7. [RequireComponent(typeof(Rigidbody))]
  8. public class PlayerController : NetworkBehaviour
  9. {
  10. public enum GroundState : byte { Jumping, Falling, Grounded }
  11. [Header("Avatar Components")]
  12. public CharacterController characterController;
  13. [Header("Movement")]
  14. [Range(1, 20)]
  15. public float moveSpeedMultiplier = 8f;
  16. [Header("Turning")]
  17. [Range(1f, 200f)]
  18. public float maxTurnSpeed = 100f;
  19. [Range(.5f, 5f)]
  20. public float turnDelta = 3f;
  21. [Header("Jumping")]
  22. [Range(0.1f, 1f)]
  23. public float initialJumpSpeed = 0.2f;
  24. [Range(1f, 10f)]
  25. public float maxJumpSpeed = 5f;
  26. [Range(0.1f, 1f)]
  27. public float jumpDelta = 0.2f;
  28. [Header("Diagnostics - Do Not Modify")]
  29. public GroundState groundState = GroundState.Grounded;
  30. [Range(-1f, 1f)]
  31. public float horizontal;
  32. [Range(-1f, 1f)]
  33. public float vertical;
  34. [Range(-200f, 200f)]
  35. public float turnSpeed;
  36. [Range(-10f, 10f)]
  37. public float jumpSpeed;
  38. [Range(-1.5f, 1.5f)]
  39. public float animVelocity;
  40. [Range(-1.5f, 1.5f)]
  41. public float animRotation;
  42. public Vector3Int velocity;
  43. public Vector3 direction;
  44. protected override void OnValidate()
  45. {
  46. base.OnValidate();
  47. if (characterController == null)
  48. characterController = GetComponent<CharacterController>();
  49. // Override CharacterController default values
  50. characterController.enabled = false;
  51. characterController.skinWidth = 0.02f;
  52. characterController.minMoveDistance = 0f;
  53. GetComponent<Rigidbody>().isKinematic = true;
  54. this.enabled = false;
  55. }
  56. public override void OnStartAuthority()
  57. {
  58. characterController.enabled = true;
  59. this.enabled = true;
  60. }
  61. public override void OnStopAuthority()
  62. {
  63. this.enabled = false;
  64. characterController.enabled = false;
  65. }
  66. void Update()
  67. {
  68. if (!characterController.enabled)
  69. return;
  70. // we need to detect player exiting vehichle, so input detection is not blockeed
  71. HandleInput();
  72. if (!canControlPlayer)
  73. return;
  74. HandleTurning();
  75. HandleJumping();
  76. HandleMove();
  77. // Reset ground state
  78. if (characterController.isGrounded)
  79. groundState = GroundState.Grounded;
  80. else if (groundState != GroundState.Jumping)
  81. groundState = GroundState.Falling;
  82. // Diagnostic velocity...FloorToInt for display purposes
  83. velocity = Vector3Int.FloorToInt(characterController.velocity);
  84. }
  85. // TODO: Turning works while airborne...feature?
  86. void HandleTurning()
  87. {
  88. // Q and E cancel each other out, reducing the turn to zero.
  89. if (Input.GetKey(KeyCode.Q))
  90. turnSpeed = Mathf.MoveTowards(turnSpeed, -maxTurnSpeed, turnDelta);
  91. if (Input.GetKey(KeyCode.E))
  92. turnSpeed = Mathf.MoveTowards(turnSpeed, maxTurnSpeed, turnDelta);
  93. // If both pressed, reduce turning speed toward zero.
  94. if (Input.GetKey(KeyCode.Q) && Input.GetKey(KeyCode.E))
  95. turnSpeed = Mathf.MoveTowards(turnSpeed, 0, turnDelta);
  96. // If neither pressed, reduce turning speed toward zero.
  97. if (!Input.GetKey(KeyCode.Q) && !Input.GetKey(KeyCode.E))
  98. turnSpeed = Mathf.MoveTowards(turnSpeed, 0, turnDelta);
  99. transform.Rotate(0f, turnSpeed * Time.deltaTime, 0f);
  100. }
  101. void HandleJumping()
  102. {
  103. // Handle variable force jumping.
  104. // Jump starts with initial power on takeoff, and jumps higher / longer
  105. // as player holds spacebar. Jump power is increased by a diminishing amout
  106. // every frame until it reaches maxJumpSpeed, or player releases the spacebar,
  107. // and then changes to the falling state until it gets grounded.
  108. if (groundState != GroundState.Falling && Input.GetKey(KeyCode.Space))
  109. {
  110. if (groundState != GroundState.Jumping)
  111. {
  112. // Start jump at initial power.
  113. groundState = GroundState.Jumping;
  114. jumpSpeed = initialJumpSpeed;
  115. }
  116. else
  117. // Jumping has already started...increase power toward maxJumpSpeed over time.
  118. jumpSpeed = Mathf.MoveTowards(jumpSpeed, maxJumpSpeed, jumpDelta);
  119. // If power has reached maxJumpSpeed, change to falling until grounded.
  120. // This prevents over-applying jump power while already in the air.
  121. if (jumpSpeed == maxJumpSpeed)
  122. groundState = GroundState.Falling;
  123. }
  124. else if (groundState != GroundState.Grounded)
  125. {
  126. // handles running off a cliff and/or player released Spacebar.
  127. groundState = GroundState.Falling;
  128. jumpSpeed = Mathf.Min(jumpSpeed, maxJumpSpeed);
  129. jumpSpeed += Physics.gravity.y * Time.deltaTime;
  130. }
  131. else
  132. jumpSpeed = Physics.gravity.y * Time.deltaTime;
  133. }
  134. // TODO: Directional input works while airborne...feature?
  135. void HandleMove()
  136. {
  137. // Capture inputs
  138. horizontal = Input.GetAxis("Horizontal");
  139. vertical = Input.GetAxis("Vertical");
  140. // Create initial direction vector without jumpSpeed (y-axis).
  141. direction = new Vector3(horizontal, 0f, vertical);
  142. // Clamp so diagonal strafing isn't a speed advantage.
  143. direction = Vector3.ClampMagnitude(direction, 1f);
  144. // Transforms direction from local space to world space.
  145. direction = transform.TransformDirection(direction);
  146. // Multiply for desired ground speed.
  147. direction *= moveSpeedMultiplier;
  148. // Add jumpSpeed to direction as last step.
  149. direction.y = jumpSpeed;
  150. // Finally move the character.
  151. characterController.Move(direction * Time.deltaTime);
  152. }
  153. public TankController tankController;
  154. // we dont want this object to move once you have control of tank
  155. public bool canControlPlayer = true;
  156. void HandleInput()
  157. {
  158. if (tankController)
  159. {
  160. // if no one owns trigger object
  161. if (canControlPlayer && tankController.objectOwner == null)
  162. {
  163. if (Input.GetKeyDown(KeyCode.E))
  164. {
  165. CmdAssignAuthority(tankController.netIdentity);
  166. }
  167. }
  168. else
  169. {
  170. // if we do own
  171. if (Input.GetKeyDown(KeyCode.Q))
  172. {
  173. CmdRemoveAuthority(tankController.netIdentity);
  174. }
  175. }
  176. // alternatively we could tell everyone to locally do this and disable NetworkTransform
  177. // it would be more optimal but requires a lil more code
  178. if (tankController.objectOwner == netIdentity)
  179. {
  180. this.transform.position = tankController.seatPosition.position;
  181. }
  182. }
  183. }
  184. void OnTriggerEnter(Collider other)
  185. {
  186. if (!isOwned) return;
  187. //Debug.Log(name + "- OnTriggerEnter - " + other.name);
  188. if (other.name == "TankTrigger")
  189. {
  190. // dont update tank variable if we're in one
  191. if (canControlPlayer)
  192. {
  193. tankController = other.transform.root.GetComponent<TankController>();
  194. }
  195. }
  196. }
  197. void OnTriggerExit(Collider other)
  198. {
  199. if (!isOwned) return;
  200. //Debug.Log(name + "- OnTriggerExit - " + other.name);
  201. if (other.name == "TankTrigger")
  202. {
  203. if (tankController)
  204. {
  205. if (tankController.objectOwner != netIdentity)
  206. {
  207. tankController = null;
  208. }
  209. }
  210. }
  211. }
  212. [Command]
  213. public void CmdAssignAuthority(NetworkIdentity _networkIdentity)
  214. {
  215. // Debug.Log("Mirror Object owner set to: " + this.netIdentity);
  216. tankController = _networkIdentity.GetComponent<TankController>();
  217. // so we dont assign it to same person again
  218. if (tankController.objectOwner != this.netIdentity)
  219. {
  220. // commands are a good place to do additional validation/cheat checks, but these are left out for simplicity here
  221. _networkIdentity.RemoveClientAuthority();
  222. _networkIdentity.AssignClientAuthority(connectionToClient);
  223. tankController.objectOwner = this.netIdentity;
  224. }
  225. }
  226. [Command]
  227. public void CmdRemoveAuthority(NetworkIdentity _networkIdentity)
  228. {
  229. //Debug.Log("Mirror Object owner removed from: " + connectionToClient.identity);
  230. tankController = _networkIdentity.GetComponent<TankController>();
  231. // double check command is sent to remove auth, from owner of object
  232. if (tankController.objectOwner != null && tankController.objectOwner == this.netIdentity)
  233. {
  234. _networkIdentity.RemoveClientAuthority();
  235. tankController.objectOwner = null;
  236. }
  237. }
  238. }
  239. }