PageRenderTime 60ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 1ms

/Scripts/SmoothFollowCamera.js

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