Advertisement
Guest User

Untitled

a guest
Jun 29th, 2017
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.19 KB | None | 0 0
  1. using UnityEngine;
  2.  
  3. // Requires the MousePointer script if you plan on having the mouse be restored to its former position when released from being locked.
  4.  
  5. /// <summary>A Camera Controller that allows the user to pan/orbit and zoom around a target with their mouse.</summary>
  6. [DisallowMultipleComponent, AddComponentMenu("Camera Controllers/Better Mouse Orbit")]
  7. public class BetterMouseOrbit : MonoBehaviour {
  8.  
  9. #region Enumerations
  10.  
  11. /// <summary>
  12. /// An enumeration of possible MouseButtons combinations for panning.
  13. /// <para>See BetterMouseOribit's panMouseButton property.</para>
  14. /// </summary>
  15. public enum MouseButton {
  16. None,
  17. AnyMouseButton,
  18.  
  19. LeftMouseButton,
  20. RightMouseButton,
  21. MiddleMouseButton,
  22.  
  23. LeftOrRightMouseButton,
  24. LeftAndRightMouseButton,
  25. }
  26.  
  27. #endregion Enumerations
  28.  
  29. #region Camera Target Settings
  30.  
  31. /// <summary>The target that the camera is looking at and following.</summary>
  32. [Header("Camera Target Settings"), Tooltip("The target that the camera is looking at and following.")]
  33. public Transform target;
  34.  
  35. /// <summary>Controls the offset from the target's center.</summary>
  36. [Tooltip("Controls the offset from the target's center.")]
  37. public Vector3 targetOffset = Vector3.up * .5f;
  38.  
  39. /// <summary>Controls whether or not the camera should inherit the target's rotation.</summary>
  40. [Tooltip("Should the camera be positioned relative to the target's rotation?")]
  41. public bool inheritTargetRotation = false;
  42.  
  43. #endregion Camera Target Settings
  44.  
  45. #region Camera Distance & Zoom Settings
  46.  
  47. /// <summary>
  48. /// Controls the desired distance away from the target.
  49. /// <para>Changes when the user scrolls the mouse's scrollwheel.</para>
  50. /// </summary>
  51. [Header("Camera Distance & Zoom Settings"), Tooltip("The distance the camera is away from the target.")]
  52. public float distance = 8f;
  53.  
  54. /// <summary>Represents the *true* distance from the target. Always being lerped towards the desired distance if not obstructed by a raycast.</summary>
  55. public float _distance { get; private set; }
  56.  
  57. /// <summary>The closest the camera may be from the target.</summary>
  58. [Tooltip("The closest the camera may be from the target.")]
  59. public float distanceMin = 0f;
  60.  
  61. /// <summary>The furthest the camera may be from the target.</summary>
  62. [Tooltip("The furthest the camera may be from the target.")]
  63. public float distanceMax = 15f;
  64.  
  65. /// <summary>A multiplier that controls how fast the desired distance can change per delta of the mouse scrollwheel.</summary>
  66. [Space, Tooltip("A multiplier that controls how fast the desired distance can change per delta of the mouse scrollwheel.")]
  67. public float zoomSpeed = 8f;
  68.  
  69. /// <summary>The speed in which the camera's true distance lerps towards its desired distance.</summary>
  70. [Tooltip("The speed in which the camera's true distance lerps towards its desired distance.")]
  71. public float zoomLerpSpeed = 20f;
  72.  
  73. /// <summary>The speed in which the camera's true distance lerps towards its desired distance when the raycast behind the camera hits a surface.</summary>
  74. [Tooltip("The speed in which the camera's true distance lerps towards its desired distance when the raycast behind the camera hits a surface.")]
  75. public float zoomLerpClipSpeed = 50f;
  76.  
  77. #endregion Camera Distance & Zoom Settings
  78.  
  79. #region Camera Orientation & Pan Settings
  80.  
  81. /// <summary>
  82. /// Controls the desired orientation of the camera relative to the target.
  83. /// <para>Changes when the user pans the camera by activating the panMouseButton and moving their cursor.</para>
  84. /// </summary>
  85. [Header("Camera Orientation & Pan Settings"), Tooltip("The orientation of the camera relative to the target.")]
  86. public Vector2 orientation = Vector2.zero;
  87.  
  88. /// <summary>Represents the *true* orientation of the camera. Always being lerped towards the desired orientation.</summary>
  89. public Vector2 _orientation { get; private set; }
  90.  
  91. /// <summary>Restricts both orientations' y- axis from going below this value.</summary>
  92. [Tooltip("Restricts both orientations' y- axis from going below this value.")]
  93. public float orientationYMin = -20f;
  94.  
  95. /// <summary>Restricts both orientations' y- axis from exceeding this value.</summary>
  96. [Tooltip("Restricts both orientations' y- axis from exceeding this value.")]
  97. public float orientationYMax = 80f;
  98.  
  99. /// <summary>A multiplier that controls how fast the desired orientation can change per delta of mouse movement.</summary>
  100. [Space, Tooltip("A multiplier that controls how fast the desired orientation can change per delta of mouse movement.")]
  101. public Vector2 panSpeed = Vector2.one * 200f;
  102.  
  103. /// <summary>The speed in which the camera's true orientation lerps towards the desired orientation.</summary>
  104. [Tooltip("The speed in which the camera's true orientation lerps towards the desired orientation.")]
  105. public float panLerpSpeed = 50f;
  106.  
  107. /// <summary>The mouse button combination that must be pressed before panning takes place.</summary>
  108. [Tooltip("Controls the mouse button combination that must be pressed before panning takes place.")]
  109. public MouseButton panMouseButton = MouseButton.LeftOrRightMouseButton;
  110.  
  111. /// <summary>A property that is true while the configured panMouseButton combination is pressed.</summary>
  112. private bool IsMouseButtonHeld {
  113. get {
  114. // switch depending on the configured mouse button:
  115. switch (panMouseButton) {
  116. // always being held if not configured:
  117. case MouseButton.None: return true;
  118. // true if any of the three mouse buttons are held:
  119. case MouseButton.AnyMouseButton:
  120. return Input.GetMouseButton(0)
  121. || Input.GetMouseButton(1)
  122. || Input.GetMouseButton(2);
  123.  
  124. // true if the left mouse button is held:
  125. case MouseButton.LeftMouseButton: return Input.GetMouseButton(0);
  126. // true if the right mouse button is held:
  127. case MouseButton.RightMouseButton: return Input.GetMouseButton(1);
  128. // true if the middle mouse button is held:
  129. case MouseButton.MiddleMouseButton: return Input.GetMouseButton(2);
  130.  
  131. // true if either the left or right mouse button is held:
  132. case MouseButton.LeftOrRightMouseButton:
  133. return Input.GetMouseButton(0)
  134. || Input.GetMouseButton(1);
  135. // true if both the left and right mouse button are held:
  136. case MouseButton.LeftAndRightMouseButton:
  137. return Input.GetMouseButton(0)
  138. && Input.GetMouseButton(1);
  139.  
  140. // false if unrecognized mouse button configuration:
  141. default: return false;
  142. }
  143. }
  144. }
  145.  
  146. #endregion Camera Orientation & Pan Settings
  147.  
  148. #region Mouse Pointer Locking Settings
  149.  
  150. /// <summary>Controls whether or not the mouse pointer should be locked while panning.</summary>
  151. [Header("Mouse Lock Settings"), Tooltip("Should the mouse pointer be locked while panning?")]
  152. public bool lockWhilePanning = true;
  153.  
  154. /// <summary>
  155. /// Controls whether or not the mouse pointer should be centered when released after being locked.
  156. /// <para>If false, only restores the mouse to its former position on windows.</para>
  157. /// </summary>
  158. [Tooltip("Should the mouse pointer be placed at the center of the game window when released after being locked?")]
  159. public bool centerMouseAfterLock = false;
  160.  
  161. #endregion Mouse Pointer Locking Settings
  162.  
  163. #region Camera Distance Raycast Settings
  164.  
  165. /// <summary>Controls the distance the camera should be away from the surface of a raycast hit.</summary>
  166. [Header("Camera Distance Raycast Settings"), Tooltip("Controls the distance the camera should be away from the surface of a raycast hit.")]
  167. public float minSurfaceDistance = 0.3f;
  168.  
  169. /// <summary>Controls which layers are valid for a raycast hit (Default: -1, Everything)</summary>
  170. [Tooltip("Controls which layers are valid for a raycast hit.")]
  171. public LayerMask raycastLayerMask = -1;
  172.  
  173. /// <summary>If enabled, excludes the target's layer from the raycast at runtime.</summary>
  174. [Tooltip("Should the camera exclude the target's layer from the raycast automatically?")]
  175. public bool excludeTargetLayer = false;
  176.  
  177. #endregion Camera Distance Raycast Settings
  178.  
  179. #region Camera Controller Methods
  180.  
  181. /// <summary>Updates the camera's position values given a delta of time.</summary>
  182. /// <param name="deltaTime">
  183. /// A parameter that represents how much time has passed since the last call.
  184. /// <para>Used to render the position the camera should be at immediately if 1.0f</para>
  185. /// </param>
  186. private void MoveCamera(float deltaTime) {
  187. // store the target's position (or Vector3.zero if no target is configured):
  188. Vector3 targetPosition = target ? target.position : Vector3.zero;
  189. // store the target's layer (or none if no target is configured):
  190. LayerMask targetLayer = target ? 1 << target.gameObject.layer : 0;
  191. // store the target's rotation (or a neutral rotation if no target is configured or if not configured to inherit the target's rotation):
  192. Quaternion targetRotation = inheritTargetRotation && target ? target.rotation : Quaternion.identity;
  193.  
  194. // update the target offset:
  195. targetPosition += targetRotation * targetOffset;
  196.  
  197. // normalize and constrain both the desired and true orientation's x and y axis:
  198. orientation = new Vector2(LoopAngle(orientation.x), ClampAngle(orientation.y, orientationYMin, orientationYMax));
  199. _orientation = new Vector2(LoopAngle(_orientation.x), ClampAngle(_orientation.y, orientationYMin, orientationYMax));
  200. // constrain both the desired and true distance:
  201. distance = Mathf.Clamp(distance, distanceMin, distanceMax);
  202. _distance = Mathf.Clamp(_distance, distanceMin, distanceMax);
  203.  
  204. // store current distance for later during logic handling the movement after a raycast hits something:
  205. float prevDistance = _distance;
  206.  
  207. // lerp the true orientation towards the desired orientation:
  208. _orientation = new Vector2(
  209. Mathf.LerpAngle(_orientation.x, orientation.x, Mathf.Clamp01(deltaTime * panLerpSpeed)),
  210. Mathf.LerpAngle(_orientation.y, orientation.y, Mathf.Clamp01(deltaTime * panLerpSpeed))
  211. );
  212. // lerp the true distance towards the desired distance:
  213. _distance = Mathf.Lerp(_distance, distance, Mathf.Clamp01(deltaTime * zoomLerpSpeed));
  214.  
  215. // store the desired orientation as a quaternion:
  216. Quaternion rotation = Quaternion.Euler(_orientation.y, _orientation.x, 0);
  217. // create a ray that is pointing back and away from the target:
  218. Vector3 ray = targetRotation * rotation * Vector3.back;
  219.  
  220. // prepare to capture a raycast hit:
  221. RaycastHit hit;
  222. // store the layermask of what counts as a valid raycast hit (and exclude the target's layer if exclude target layer is configured):
  223. LayerMask layerMask = raycastLayerMask & (excludeTargetLayer ? ~targetLayer : -1);
  224.  
  225. // spherecast (which is a raycast but with a minimum surface distance) away from the target along the ray
  226. // to prevent the target from being obstructed and the camera from clipping into a surface:
  227. if (Physics.SphereCast(targetPosition, minSurfaceDistance, ray, out hit, distance, layerMask)) {
  228. // recalculate the lerping of the true distance towards the desired distance, this time accounting for the raycast hit:
  229. _distance = Mathf.Lerp(prevDistance, hit.distance, Mathf.Clamp01(deltaTime * zoomLerpClipSpeed));
  230. }
  231.  
  232. // update the camera positoin and rotation:
  233. transform.position = targetPosition + (ray * _distance);
  234. transform.rotation = targetRotation * rotation;
  235.  
  236. //// draw debug rays to show the desired distance (red) and the true distance (white) of the camera in the scene view:
  237. //Debug.DrawRay(targetPosition, ray * distance, Color.red);
  238. //Debug.DrawRay(targetPosition, ray * _distance, Color.white);
  239. }
  240.  
  241. /// <summary>
  242. /// Called by Unity when the component values in the inspector are changed.
  243. /// <para>Used to reposition the Camera while editing the scene.</para>
  244. /// </summary>
  245. private void OnValidate() {
  246. // force the true orientation and distance to their desired values:
  247. _orientation = orientation;
  248. _distance = distance;
  249.  
  250. // immediately move the camera to where it should be without lerping:
  251. MoveCamera(1f);
  252. }
  253.  
  254. /// <summary>Awake is called once when script instance is being loaded. See Unity3D docs.</summary>
  255. private void Awake() {
  256. // setup the true orientation and distance:
  257. _orientation = orientation;
  258. _distance = distance;
  259. }
  260.  
  261. /// <summary>LateUpdate is called every frame, if the component is enabled. See Unity3D docs.</summary>
  262. private void LateUpdate() {
  263. // store true distance and orientation for delta checking:
  264. float prevDistance = _distance;
  265. Vector2 prevOrientation = _orientation;
  266.  
  267. // if we should lock the mouse cursor while panning:
  268. if (lockWhilePanning) {
  269. // if the mouse shouldn't be centered after the pan is complete:
  270. if (!centerMouseAfterLock) {
  271. // using the MousePointer class means the mouse position will be restored on release:
  272. MousePointer.lockState = IsMouseButtonHeld ? CursorLockMode.Locked : CursorLockMode.None;
  273. MousePointer.visible = !IsMouseButtonHeld;
  274. } else {
  275. // using the default Unity3D cursor class means the mouse will be centered on release:
  276. Cursor.lockState = IsMouseButtonHeld ? CursorLockMode.Locked : CursorLockMode.None;
  277. Cursor.visible = !IsMouseButtonHeld;
  278. }
  279. }
  280.  
  281. // if the configured mouse button combination is pressed, then pan the camera's desired orientation by the mouse's movement deltas:
  282. if (IsMouseButtonHeld) {
  283. orientation += new Vector2(
  284. Input.GetAxis("Mouse X") * panSpeed.x,
  285. -Input.GetAxis("Mouse Y") * panSpeed.y) * Time.deltaTime;
  286. }
  287.  
  288. // change the camera's desired distance by the mouses's scrollwheel delta:
  289. distance -= Input.GetAxis("Mouse ScrollWheel") * zoomSpeed;
  290.  
  291. // move the camera's real orientation and distance towards their desired values given the delta of time since the last frame:
  292. MoveCamera(Time.deltaTime);
  293. }
  294.  
  295. /// <summary>A helper function that takes an angle and returns what it would be if it was looped between 0 and 360.</summary>
  296. private static float LoopAngle(float angle) {
  297. return Mathf.Repeat(angle, 360);
  298. }
  299.  
  300. /// <summary>A helper function that takes an angle and returns it clamped between a minimum and maximum.</summary>
  301. private static float ClampAngle(float angle, float min, float max) {
  302. if (angle < -360F) angle += 360F;
  303. if (angle > 360F) angle -= 360F;
  304. return Mathf.Clamp(angle, min, max);
  305. }
  306.  
  307. #endregion Camera Controller Methods
  308. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement