SimpleCameraController.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #if ENABLE_INPUT_SYSTEM
  2. using UnityEngine.InputSystem;
  3. #endif
  4. using UnityEngine;
  5. namespace UnityTemplateProjects
  6. {
  7. public class SimpleCameraController : MonoBehaviour
  8. {
  9. class CameraState
  10. {
  11. public float yaw;
  12. public float pitch;
  13. public float roll;
  14. public float x;
  15. public float y;
  16. public float z;
  17. public void SetFromTransform(Transform t)
  18. {
  19. pitch = t.eulerAngles.x;
  20. yaw = t.eulerAngles.y;
  21. roll = t.eulerAngles.z;
  22. x = t.position.x;
  23. y = t.position.y;
  24. z = t.position.z;
  25. }
  26. public void Translate(Vector3 translation)
  27. {
  28. Vector3 rotatedTranslation = Quaternion.Euler(pitch, yaw, roll) * translation;
  29. x += rotatedTranslation.x;
  30. y += rotatedTranslation.y;
  31. z += rotatedTranslation.z;
  32. }
  33. public void LerpTowards(CameraState target, float positionLerpPct, float rotationLerpPct)
  34. {
  35. yaw = Mathf.Lerp(yaw, target.yaw, rotationLerpPct);
  36. pitch = Mathf.Lerp(pitch, target.pitch, rotationLerpPct);
  37. roll = Mathf.Lerp(roll, target.roll, rotationLerpPct);
  38. x = Mathf.Lerp(x, target.x, positionLerpPct);
  39. y = Mathf.Lerp(y, target.y, positionLerpPct);
  40. z = Mathf.Lerp(z, target.z, positionLerpPct);
  41. }
  42. public void UpdateTransform(Transform t)
  43. {
  44. t.eulerAngles = new Vector3(pitch, yaw, roll);
  45. t.position = new Vector3(x, y, z);
  46. }
  47. }
  48. const float k_MouseSensitivityMultiplier = 0.01f;
  49. CameraState m_TargetCameraState = new CameraState();
  50. CameraState m_InterpolatingCameraState = new CameraState();
  51. [Header("Movement Settings")]
  52. [Tooltip("Exponential boost factor on translation, controllable by mouse wheel.")]
  53. public float boost = 3.5f;
  54. [Tooltip("Time it takes to interpolate camera position 99% of the way to the target."), Range(0.001f, 1f)]
  55. public float positionLerpTime = 0.2f;
  56. [Header("Rotation Settings")]
  57. [Tooltip("Multiplier for the sensitivity of the rotation.")]
  58. public float mouseSensitivity = 60.0f;
  59. [Tooltip("X = Change in mouse position.\nY = Multiplicative factor for camera rotation.")]
  60. public AnimationCurve mouseSensitivityCurve = new AnimationCurve(new Keyframe(0f, 0.5f, 0f, 5f), new Keyframe(1f, 2.5f, 0f, 0f));
  61. [Tooltip("Time it takes to interpolate camera rotation 99% of the way to the target."), Range(0.001f, 1f)]
  62. public float rotationLerpTime = 0.01f;
  63. [Tooltip("Whether or not to invert our Y axis for mouse input to rotation.")]
  64. public bool invertY = false;
  65. #if ENABLE_INPUT_SYSTEM
  66. InputAction movementAction;
  67. InputAction verticalMovementAction;
  68. InputAction lookAction;
  69. InputAction boostFactorAction;
  70. bool mouseRightButtonPressed;
  71. void Start()
  72. {
  73. var map = new InputActionMap("Simple Camera Controller");
  74. lookAction = map.AddAction("look", binding: "<Mouse>/delta");
  75. movementAction = map.AddAction("move", binding: "<Gamepad>/leftStick");
  76. verticalMovementAction = map.AddAction("Vertical Movement");
  77. boostFactorAction = map.AddAction("Boost Factor", binding: "<Mouse>/scroll");
  78. lookAction.AddBinding("<Gamepad>/rightStick").WithProcessor("scaleVector2(x=15, y=15)");
  79. movementAction.AddCompositeBinding("Dpad")
  80. .With("Up", "<Keyboard>/w")
  81. .With("Up", "<Keyboard>/upArrow")
  82. .With("Down", "<Keyboard>/s")
  83. .With("Down", "<Keyboard>/downArrow")
  84. .With("Left", "<Keyboard>/a")
  85. .With("Left", "<Keyboard>/leftArrow")
  86. .With("Right", "<Keyboard>/d")
  87. .With("Right", "<Keyboard>/rightArrow");
  88. verticalMovementAction.AddCompositeBinding("Dpad")
  89. .With("Up", "<Keyboard>/pageUp")
  90. .With("Down", "<Keyboard>/pageDown")
  91. .With("Up", "<Keyboard>/e")
  92. .With("Down", "<Keyboard>/q")
  93. .With("Up", "<Gamepad>/rightshoulder")
  94. .With("Down", "<Gamepad>/leftshoulder");
  95. boostFactorAction.AddBinding("<Gamepad>/Dpad").WithProcessor("scaleVector2(x=1, y=4)");
  96. movementAction.Enable();
  97. lookAction.Enable();
  98. verticalMovementAction.Enable();
  99. boostFactorAction.Enable();
  100. }
  101. #endif
  102. void OnEnable()
  103. {
  104. m_TargetCameraState.SetFromTransform(transform);
  105. m_InterpolatingCameraState.SetFromTransform(transform);
  106. }
  107. Vector3 GetInputTranslationDirection()
  108. {
  109. Vector3 direction = Vector3.zero;
  110. #if ENABLE_INPUT_SYSTEM
  111. var moveDelta = movementAction.ReadValue<Vector2>();
  112. direction.x = moveDelta.x;
  113. direction.z = moveDelta.y;
  114. direction.y = verticalMovementAction.ReadValue<Vector2>().y;
  115. #else
  116. if (Input.GetKey(KeyCode.W))
  117. {
  118. direction += Vector3.forward;
  119. }
  120. if (Input.GetKey(KeyCode.S))
  121. {
  122. direction += Vector3.back;
  123. }
  124. if (Input.GetKey(KeyCode.A))
  125. {
  126. direction += Vector3.left;
  127. }
  128. if (Input.GetKey(KeyCode.D))
  129. {
  130. direction += Vector3.right;
  131. }
  132. if (Input.GetKey(KeyCode.Q))
  133. {
  134. direction += Vector3.down;
  135. }
  136. if (Input.GetKey(KeyCode.E))
  137. {
  138. direction += Vector3.up;
  139. }
  140. #endif
  141. return direction;
  142. }
  143. void Update()
  144. {
  145. // Exit Sample
  146. if (IsEscapePressed())
  147. {
  148. Application.Quit();
  149. #if UNITY_EDITOR
  150. UnityEditor.EditorApplication.isPlaying = false;
  151. #endif
  152. }
  153. // Hide and lock cursor when right mouse button pressed
  154. if (IsRightMouseButtonDown())
  155. {
  156. Cursor.lockState = CursorLockMode.Locked;
  157. }
  158. // Unlock and show cursor when right mouse button released
  159. if (IsRightMouseButtonUp())
  160. {
  161. Cursor.visible = true;
  162. Cursor.lockState = CursorLockMode.None;
  163. }
  164. // Rotation
  165. if (IsCameraRotationAllowed())
  166. {
  167. var mouseMovement = GetInputLookRotation() * k_MouseSensitivityMultiplier * mouseSensitivity;
  168. if (invertY)
  169. mouseMovement.y = -mouseMovement.y;
  170. var mouseSensitivityFactor = mouseSensitivityCurve.Evaluate(mouseMovement.magnitude);
  171. m_TargetCameraState.yaw += mouseMovement.x * mouseSensitivityFactor;
  172. m_TargetCameraState.pitch += mouseMovement.y * mouseSensitivityFactor;
  173. }
  174. // Translation
  175. var translation = GetInputTranslationDirection() * Time.deltaTime;
  176. // Speed up movement when shift key held
  177. if (IsBoostPressed())
  178. {
  179. translation *= 10.0f;
  180. }
  181. // Modify movement by a boost factor (defined in Inspector and modified in play mode through the mouse scroll wheel)
  182. boost += GetBoostFactor();
  183. translation *= Mathf.Pow(2.0f, boost);
  184. m_TargetCameraState.Translate(translation);
  185. // Framerate-independent interpolation
  186. // Calculate the lerp amount, such that we get 99% of the way to our target in the specified time
  187. var positionLerpPct = 1f - Mathf.Exp((Mathf.Log(1f - 0.99f) / positionLerpTime) * Time.deltaTime);
  188. var rotationLerpPct = 1f - Mathf.Exp((Mathf.Log(1f - 0.99f) / rotationLerpTime) * Time.deltaTime);
  189. m_InterpolatingCameraState.LerpTowards(m_TargetCameraState, positionLerpPct, rotationLerpPct);
  190. m_InterpolatingCameraState.UpdateTransform(transform);
  191. }
  192. float GetBoostFactor()
  193. {
  194. #if ENABLE_INPUT_SYSTEM
  195. return boostFactorAction.ReadValue<Vector2>().y * 0.01f;
  196. #else
  197. return Input.mouseScrollDelta.y * 0.01f;
  198. #endif
  199. }
  200. Vector2 GetInputLookRotation()
  201. {
  202. // try to compensate the diff between the two input systems by multiplying with empirical values
  203. #if ENABLE_INPUT_SYSTEM
  204. var delta = lookAction.ReadValue<Vector2>();
  205. delta *= 0.5f; // Account for scaling applied directly in Windows code by old input system.
  206. delta *= 0.1f; // Account for sensitivity setting on old Mouse X and Y axes.
  207. return delta;
  208. #else
  209. return new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
  210. #endif
  211. }
  212. bool IsBoostPressed()
  213. {
  214. #if ENABLE_INPUT_SYSTEM
  215. bool boost = Keyboard.current != null ? Keyboard.current.leftShiftKey.isPressed : false;
  216. boost |= Gamepad.current != null ? Gamepad.current.xButton.isPressed : false;
  217. return boost;
  218. #else
  219. return Input.GetKey(KeyCode.LeftShift);
  220. #endif
  221. }
  222. bool IsEscapePressed()
  223. {
  224. #if ENABLE_INPUT_SYSTEM
  225. return Keyboard.current != null ? Keyboard.current.escapeKey.isPressed : false;
  226. #else
  227. return Input.GetKey(KeyCode.Escape);
  228. #endif
  229. }
  230. bool IsCameraRotationAllowed()
  231. {
  232. #if ENABLE_INPUT_SYSTEM
  233. bool canRotate = Mouse.current != null ? Mouse.current.rightButton.isPressed : false;
  234. canRotate |= Gamepad.current != null ? Gamepad.current.rightStick.ReadValue().magnitude > 0 : false;
  235. return canRotate;
  236. #else
  237. return Input.GetMouseButton(1);
  238. #endif
  239. }
  240. bool IsRightMouseButtonDown()
  241. {
  242. #if ENABLE_INPUT_SYSTEM
  243. return Mouse.current != null ? Mouse.current.rightButton.isPressed : false;
  244. #else
  245. return Input.GetMouseButtonDown(1);
  246. #endif
  247. }
  248. bool IsRightMouseButtonUp()
  249. {
  250. #if ENABLE_INPUT_SYSTEM
  251. return Mouse.current != null ? !Mouse.current.rightButton.isPressed : false;
  252. #else
  253. return Input.GetMouseButtonUp(1);
  254. #endif
  255. }
  256. }
  257. }