1. using UnityEngine;
    2. using System.Collections;
    3. public class ThirdPersonCamera : MonoBehaviour {
    4. public Transform cameraTransform;
    5. private Transform _target;
    6. public float distance = 7.0f;
    7. public float height = 3.0f;
    8. public float angularSmoothLag = 0.3f;
    9. public float angularMaxSpeed = 15.0f;
    10. public float heightSmoothLag = 0.3f;
    11. public float snapSmoothLag = 0.2f;
    12. public float snapMaxSpeed = 720.0f;
    13. public float clampHeadPositionScreenSpace = 0.75f;
    14. public float lockCameraTimeout = 0.2f;
    15. private Vector3 headOffset = Vector3.zero;
    16. private Vector3 centerOffset = Vector3.zero;
    17. private float heightVelocity = 0.0f;
    18. private float angleVelocity = 0.0f;
    19. private bool snap = false;
    20. private ThirdPersonController controller;
    21. private float targetHeight = 100000.0f;
    22. void Awake ()
    23. {
    24. if(!cameraTransform && Camera.main)
    25. cameraTransform = Camera.main.transform;
    26. if(!cameraTransform) {
    27. Debug.Log("Please assign a camera to the ThirdPersonCamera script.");
    28. enabled = false;
    29. }
    30. _target = transform;
    31. if (_target)
    32. {
    33. controller = _target.GetComponent<ThirdPersonController>();
    34. }
    35. if (controller)
    36. {
    37. CharacterController characterController = (CharacterController)_target.collider;
    38. centerOffset = characterController.bounds.center - _target.position;
    39. headOffset = centerOffset;
    40. headOffset.y = characterController.bounds.max.y - _target.position.y;
    41. }
    42. else
    43. Debug.Log("Please assign a target to the camera that has a ThirdPersonController script attached.");
    44. Cut(_target, centerOffset);
    45. }
    46. void DebugDrawStuff ()
    47. {
    48. Debug.DrawLine(_target.position, _target.position + headOffset);
    49. }
    50. float AngleDistance (float a , float b )
    51. {
    52. a = Mathf.Repeat(a, 360);
    53. b = Mathf.Repeat(b, 360);
    54. return Mathf.Abs(b - a);
    55. }
    56. void Apply (Transform dummyTarget, Vector3 dummyCenter)
    57. {
    58. // Early out if we don't have a target
    59. if (!controller)
    60. return;
    61. Vector3 targetCenter = _target.position + centerOffset;
    62. Vector3 targetHead = _target.position + headOffset;
    63. // DebugDrawStuff();
    64. // Calculate the current & target rotation angles
    65. float originalTargetAngle = _target.eulerAngles.y;
    66. float currentAngle = cameraTransform.eulerAngles.y;
    67. // Adjust real target angle when camera is locked
    68. float targetAngle = originalTargetAngle;
    69. // When pressing Fire2 (alt) the camera will snap to the target direction real quick.
    70. // It will stop snapping when it reaches the target
    71. if (Input.GetButton("Fire2"))
    72. snap = true;
    73. if (snap)
    74. {
    75. // We are close to the target, so we can stop snapping now!
    76. if (AngleDistance (currentAngle, originalTargetAngle) < 3.0)
    77. snap = false;
    78. currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angleVelocity, snapSmoothLag, snapMaxSpeed);
    79. }
    80. // Normal camera motion
    81. else
    82. {
    83. if (controller.GetLockCameraTimer () < lockCameraTimeout)
    84. {
    85. targetAngle = currentAngle;
    86. }
    87. // Lock the camera when moving backwards!
    88. // * It is really confusing to do 180 degree spins when turning around.
    89. if (AngleDistance (currentAngle, targetAngle) > 160 && controller.IsMovingBackwards ())
    90. targetAngle += 180;
    91. currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angleVelocity, angularSmoothLag, angularMaxSpeed);
    92. }
    93. // When jumping don't move camera upwards but only down!
    94. if (controller.IsJumping ())
    95. {
    96. // We'd be moving the camera upwards, do that only if it's really high
    97. float newTargetHeight = targetCenter.y + height;
    98. if (newTargetHeight < targetHeight || newTargetHeight - targetHeight > 5)
    99. targetHeight = targetCenter.y + height;
    100. }
    101. // When walking always update the target height
    102. else
    103. {
    104. targetHeight = targetCenter.y + height;
    105. }
    106. // Damp the height
    107. float currentHeight = cameraTransform.position.y;
    108. currentHeight = Mathf.SmoothDamp (currentHeight, targetHeight, ref heightVelocity, heightSmoothLag);
    109. // Convert the angle into a rotation, by which we then reposition the camera
    110. Quaternion currentRotation = Quaternion.Euler (0, currentAngle, 0);
    111. // Set the position of the camera on the x-z plane to:
    112. // distance meters behind the target
    113. cameraTransform.position = targetCenter;
    114. cameraTransform.position += currentRotation * Vector3.back * distance;
    115. // Set the height of the camera
    116. cameraTransform.position = new Vector3(cameraTransform.position.x,currentHeight,cameraTransform.position.z);
    117. // Always look at the target
    118. SetUpRotation(targetCenter, targetHead);
    119. }
    120. void LateUpdate () {
    121. Apply (transform, Vector3.zero);
    122. }
    123. void Cut (Transform dummyTarget , Vector3 dummyCenter)
    124. {
    125. float oldHeightSmooth = heightSmoothLag;
    126. float oldSnapMaxSpeed = snapMaxSpeed;
    127. float oldSnapSmooth = snapSmoothLag;
    128. snapMaxSpeed = 10000;
    129. snapSmoothLag = 0.001f;
    130. heightSmoothLag = 0.001f;
    131. snap = true;
    132. Apply (transform, Vector3.zero);
    133. heightSmoothLag = oldHeightSmooth;
    134. snapMaxSpeed = oldSnapMaxSpeed;
    135. snapSmoothLag = oldSnapSmooth;
    136. }
    137. void SetUpRotation (Vector3 centerPos,Vector3 headPos)
    138. {
    139. // Now it's getting hairy. The devil is in the details here, the big issue is jumping of course.
    140. // * When jumping up and down we don't want to center the guy in screen space.
    141. // This is important to give a feel for how high you jump and avoiding large camera movements.
    142. //
    143. // * At the same time we dont want him to ever go out of screen and we want all rotations to be totally smooth.
    144. //
    145. // So here is what we will do:
    146. //
    147. // 1. We first find the rotation around the y axis. Thus he is always centered on the y-axis
    148. // 2. When grounded we make him be centered
    149. // 3. When jumping we keep the camera rotation but rotate the camera to get him back into view if his head is above some threshold
    150. // 4. When landing we smoothly interpolate towards centering him on screen
    151. Vector3 cameraPos = cameraTransform.position;
    152. Vector3 offsetToCenter = centerPos - cameraPos;
    153. // Generate base rotation only around y-axis
    154. Quaternion yRotation = Quaternion.LookRotation(new Vector3(offsetToCenter.x, 0, offsetToCenter.z));
    155. Vector3 relativeOffset = Vector3.forward * distance + Vector3.down * height;
    156. cameraTransform.rotation = yRotation * Quaternion.LookRotation(relativeOffset);
    157. // Calculate the projected center position and top position in world space
    158. Ray centerRay = cameraTransform.camera.ViewportPointToRay(new Vector3(0.5f, 0.5f, 1f));
    159. Ray topRay = cameraTransform.camera.ViewportPointToRay(new Vector3(0.5f, clampHeadPositionScreenSpace, 1f));
    160. Vector3 centerRayPos = centerRay.GetPoint(distance);
    161. Vector3 topRayPos = topRay.GetPoint(distance);
    162. float centerToTopAngle = Vector3.Angle(centerRay.direction, topRay.direction);
    163. float heightToAngle = centerToTopAngle / (centerRayPos.y - topRayPos.y);
    164. float extraLookAngle = heightToAngle * (centerRayPos.y - centerPos.y);
    165. if (extraLookAngle < centerToTopAngle)
    166. {
    167. extraLookAngle = 0;
    168. }
    169. else
    170. {
    171. extraLookAngle = extraLookAngle - centerToTopAngle;
    172. cameraTransform.rotation *= Quaternion.Euler(-extraLookAngle, 0, 0);
    173. }
    174. }
    175. Vector3 GetCenterOffset ()
    176. {
    177. return centerOffset;
    178. }
    179. }