CameraController.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using UnityEngine;
  2. using System.Collections;
  3. namespace RootMotion {
  4. /// <summary>
  5. /// 3rd person camera controller.
  6. /// </summary>
  7. public class CameraController : MonoBehaviour {
  8. // When to update the camera?
  9. [System.Serializable]
  10. public enum UpdateMode {
  11. Update,
  12. FixedUpdate,
  13. LateUpdate,
  14. FixedLateUpdate
  15. }
  16. public Transform target; // The target Transform to follow
  17. public Transform rotationSpace; // If assigned, will use this Transform's rotation as the rotation space instead of the world space. Useful with spherical planets.
  18. public UpdateMode updateMode = UpdateMode.LateUpdate; // When to update the camera?
  19. public bool lockCursor = true; // If true, the mouse will be locked to screen center and hidden
  20. [Header("Position")]
  21. public bool smoothFollow; // If > 0, camera will smoothly interpolate towards the target
  22. public Vector3 offset = new Vector3(0, 1.5f, 0.5f); // The offset from target relative to camera rotation
  23. public float followSpeed = 10f; // Smooth follow speed
  24. [Header("Rotation")]
  25. public float rotationSensitivity = 3.5f; // The sensitivity of rotation
  26. public float yMinLimit = -20; // Min vertical angle
  27. public float yMaxLimit = 80; // Max vertical angle
  28. public bool rotateAlways = true; // Always rotate to mouse?
  29. public bool rotateOnLeftButton; // Rotate to mouse when left button is pressed?
  30. public bool rotateOnRightButton; // Rotate to mouse when right button is pressed?
  31. public bool rotateOnMiddleButton; // Rotate to mouse when middle button is pressed?
  32. [Header("Distance")]
  33. public float distance = 10.0f; // The current distance to target
  34. public float minDistance = 4; // The minimum distance to target
  35. public float maxDistance = 10; // The maximum distance to target
  36. public float zoomSpeed = 10f; // The speed of interpolating the distance
  37. public float zoomSensitivity = 1f; // The sensitivity of mouse zoom
  38. [Header("Blocking")]
  39. public LayerMask blockingLayers;
  40. public float blockingRadius = 1f;
  41. public float blockingSmoothTime = 0.1f;
  42. public float blockingOriginOffset;
  43. [Range(0f, 1f)] public float blockedOffset = 0.5f;
  44. public float x { get; private set; } // The current x rotation of the camera
  45. public float y { get; private set; } // The current y rotation of the camera
  46. public float distanceTarget { get; private set; } // Get/set distance
  47. private Vector3 targetDistance, position;
  48. private Quaternion rotation = Quaternion.identity;
  49. private Vector3 smoothPosition;
  50. private Camera cam;
  51. private bool fixedFrame;
  52. private float fixedDeltaTime;
  53. private Quaternion r = Quaternion.identity;
  54. private Vector3 lastUp;
  55. private float blockedDistance = 10f, blockedDistanceV;
  56. public void SetAngles(Quaternion rotation)
  57. {
  58. Vector3 euler = rotation.eulerAngles;
  59. this.x = euler.y;
  60. this.y = euler.x;
  61. }
  62. public void SetAngles(float yaw, float pitch)
  63. {
  64. this.x = yaw;
  65. this.y = pitch;
  66. }
  67. // Initiate, set the params to the current transformation of the camera relative to the target
  68. protected virtual void Awake () {
  69. Vector3 angles = transform.eulerAngles;
  70. x = angles.y;
  71. y = angles.x;
  72. distanceTarget = distance;
  73. smoothPosition = transform.position;
  74. cam = GetComponent<Camera>();
  75. lastUp = rotationSpace != null? rotationSpace.up: Vector3.up;
  76. }
  77. protected virtual void Update() {
  78. if (updateMode == UpdateMode.Update) UpdateTransform();
  79. }
  80. protected virtual void FixedUpdate() {
  81. fixedFrame = true;
  82. fixedDeltaTime += Time.deltaTime;
  83. if (updateMode == UpdateMode.FixedUpdate) UpdateTransform();
  84. }
  85. protected virtual void LateUpdate() {
  86. UpdateInput();
  87. if (updateMode == UpdateMode.LateUpdate) UpdateTransform();
  88. if (updateMode == UpdateMode.FixedLateUpdate && fixedFrame) {
  89. UpdateTransform(fixedDeltaTime);
  90. fixedDeltaTime = 0f;
  91. fixedFrame = false;
  92. }
  93. }
  94. // Read the user input
  95. public void UpdateInput() {
  96. if (!cam.enabled) return;
  97. // Cursors
  98. Cursor.lockState = lockCursor? CursorLockMode.Locked: CursorLockMode.None;
  99. Cursor.visible = lockCursor? false: true;
  100. // Should we rotate the camera?
  101. bool rotate = rotateAlways || (rotateOnLeftButton && Input.GetMouseButton(0)) || (rotateOnRightButton && Input.GetMouseButton(1)) || (rotateOnMiddleButton && Input.GetMouseButton(2));
  102. // delta rotation
  103. if (rotate) {
  104. x += Input.GetAxis("Mouse X") * rotationSensitivity;
  105. y = ClampAngle(y - Input.GetAxis("Mouse Y") * rotationSensitivity, yMinLimit, yMaxLimit);
  106. }
  107. // Distance
  108. distanceTarget = Mathf.Clamp(distanceTarget + zoomAdd, minDistance, maxDistance);
  109. }
  110. // Update the camera transform
  111. public void UpdateTransform() {
  112. UpdateTransform(Time.deltaTime);
  113. }
  114. public void UpdateTransform(float deltaTime) {
  115. if (!cam.enabled) return;
  116. // Rotation
  117. rotation = Quaternion.AngleAxis(x, Vector3.up) * Quaternion.AngleAxis(y, Vector3.right);
  118. if (rotationSpace != null) {
  119. r = Quaternion.FromToRotation(lastUp, rotationSpace.up) * r;
  120. rotation = r * rotation;
  121. lastUp = rotationSpace.up;
  122. }
  123. if (target != null) {
  124. // Distance
  125. distance += (distanceTarget - distance) * zoomSpeed * deltaTime;
  126. // Smooth follow
  127. if (!smoothFollow) smoothPosition = target.position;
  128. else smoothPosition = Vector3.Lerp(smoothPosition, target.position, deltaTime * followSpeed);
  129. // Position
  130. Vector3 t = smoothPosition + rotation * offset;
  131. Vector3 f = rotation * -Vector3.forward;
  132. if (blockingLayers != -1)
  133. {
  134. RaycastHit hit;
  135. if (Physics.SphereCast(t - f * blockingOriginOffset, blockingRadius, f, out hit, blockingOriginOffset + distanceTarget - blockingRadius, blockingLayers))
  136. {
  137. blockedDistance = Mathf.SmoothDamp(blockedDistance, hit.distance + blockingRadius * (1f - blockedOffset) - blockingOriginOffset, ref blockedDistanceV, blockingSmoothTime);
  138. }
  139. else blockedDistance = distanceTarget;
  140. distance = Mathf.Min(distance, blockedDistance);
  141. }
  142. position = t + f * distance;
  143. // Translating the camera
  144. transform.position = position;
  145. }
  146. transform.rotation = rotation;
  147. }
  148. // Zoom input
  149. private float zoomAdd {
  150. get {
  151. float scrollAxis = Input.GetAxis("Mouse ScrollWheel");
  152. if (scrollAxis > 0) return -zoomSensitivity;
  153. if (scrollAxis < 0) return zoomSensitivity;
  154. return 0;
  155. }
  156. }
  157. // Clamping Euler angles
  158. private float ClampAngle (float angle, float min, float max) {
  159. if (angle < -360) angle += 360;
  160. if (angle > 360) angle -= 360;
  161. return Mathf.Clamp (angle, min, max);
  162. }
  163. }
  164. }