Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using UnityEngine;
- // Requires the MousePointer script if you plan on having the mouse be restored to its former position when released from being locked.
- /// <summary>A Camera Controller that allows the user to pan/orbit and zoom around a target with their mouse.</summary>
- [DisallowMultipleComponent, AddComponentMenu("Camera Controllers/Better Mouse Orbit")]
- public class BetterMouseOrbit : MonoBehaviour {
- #region Enumerations
- /// <summary>
- /// An enumeration of possible MouseButtons combinations for panning.
- /// <para>See BetterMouseOribit's panMouseButton property.</para>
- /// </summary>
- public enum MouseButton {
- None,
- AnyMouseButton,
- LeftMouseButton,
- RightMouseButton,
- MiddleMouseButton,
- LeftOrRightMouseButton,
- LeftAndRightMouseButton,
- }
- #endregion Enumerations
- #region Camera Target Settings
- /// <summary>The target that the camera is looking at and following.</summary>
- [Header("Camera Target Settings"), Tooltip("The target that the camera is looking at and following.")]
- public Transform target;
- /// <summary>Controls the offset from the target's center.</summary>
- [Tooltip("Controls the offset from the target's center.")]
- public Vector3 targetOffset = Vector3.up * .5f;
- /// <summary>Controls whether or not the camera should inherit the target's rotation.</summary>
- [Tooltip("Should the camera be positioned relative to the target's rotation?")]
- public bool inheritTargetRotation = false;
- #endregion Camera Target Settings
- #region Camera Distance & Zoom Settings
- /// <summary>
- /// Controls the desired distance away from the target.
- /// <para>Changes when the user scrolls the mouse's scrollwheel.</para>
- /// </summary>
- [Header("Camera Distance & Zoom Settings"), Tooltip("The distance the camera is away from the target.")]
- public float distance = 8f;
- /// <summary>Represents the *true* distance from the target. Always being lerped towards the desired distance if not obstructed by a raycast.</summary>
- public float _distance { get; private set; }
- /// <summary>The closest the camera may be from the target.</summary>
- [Tooltip("The closest the camera may be from the target.")]
- public float distanceMin = 0f;
- /// <summary>The furthest the camera may be from the target.</summary>
- [Tooltip("The furthest the camera may be from the target.")]
- public float distanceMax = 15f;
- /// <summary>A multiplier that controls how fast the desired distance can change per delta of the mouse scrollwheel.</summary>
- [Space, Tooltip("A multiplier that controls how fast the desired distance can change per delta of the mouse scrollwheel.")]
- public float zoomSpeed = 8f;
- /// <summary>The speed in which the camera's true distance lerps towards its desired distance.</summary>
- [Tooltip("The speed in which the camera's true distance lerps towards its desired distance.")]
- public float zoomLerpSpeed = 20f;
- /// <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>
- [Tooltip("The speed in which the camera's true distance lerps towards its desired distance when the raycast behind the camera hits a surface.")]
- public float zoomLerpClipSpeed = 50f;
- #endregion Camera Distance & Zoom Settings
- #region Camera Orientation & Pan Settings
- /// <summary>
- /// Controls the desired orientation of the camera relative to the target.
- /// <para>Changes when the user pans the camera by activating the panMouseButton and moving their cursor.</para>
- /// </summary>
- [Header("Camera Orientation & Pan Settings"), Tooltip("The orientation of the camera relative to the target.")]
- public Vector2 orientation = Vector2.zero;
- /// <summary>Represents the *true* orientation of the camera. Always being lerped towards the desired orientation.</summary>
- public Vector2 _orientation { get; private set; }
- /// <summary>Restricts both orientations' y- axis from going below this value.</summary>
- [Tooltip("Restricts both orientations' y- axis from going below this value.")]
- public float orientationYMin = -20f;
- /// <summary>Restricts both orientations' y- axis from exceeding this value.</summary>
- [Tooltip("Restricts both orientations' y- axis from exceeding this value.")]
- public float orientationYMax = 80f;
- /// <summary>A multiplier that controls how fast the desired orientation can change per delta of mouse movement.</summary>
- [Space, Tooltip("A multiplier that controls how fast the desired orientation can change per delta of mouse movement.")]
- public Vector2 panSpeed = Vector2.one * 200f;
- /// <summary>The speed in which the camera's true orientation lerps towards the desired orientation.</summary>
- [Tooltip("The speed in which the camera's true orientation lerps towards the desired orientation.")]
- public float panLerpSpeed = 50f;
- /// <summary>The mouse button combination that must be pressed before panning takes place.</summary>
- [Tooltip("Controls the mouse button combination that must be pressed before panning takes place.")]
- public MouseButton panMouseButton = MouseButton.LeftOrRightMouseButton;
- /// <summary>A property that is true while the configured panMouseButton combination is pressed.</summary>
- private bool IsMouseButtonHeld {
- get {
- // switch depending on the configured mouse button:
- switch (panMouseButton) {
- // always being held if not configured:
- case MouseButton.None: return true;
- // true if any of the three mouse buttons are held:
- case MouseButton.AnyMouseButton:
- return Input.GetMouseButton(0)
- || Input.GetMouseButton(1)
- || Input.GetMouseButton(2);
- // true if the left mouse button is held:
- case MouseButton.LeftMouseButton: return Input.GetMouseButton(0);
- // true if the right mouse button is held:
- case MouseButton.RightMouseButton: return Input.GetMouseButton(1);
- // true if the middle mouse button is held:
- case MouseButton.MiddleMouseButton: return Input.GetMouseButton(2);
- // true if either the left or right mouse button is held:
- case MouseButton.LeftOrRightMouseButton:
- return Input.GetMouseButton(0)
- || Input.GetMouseButton(1);
- // true if both the left and right mouse button are held:
- case MouseButton.LeftAndRightMouseButton:
- return Input.GetMouseButton(0)
- && Input.GetMouseButton(1);
- // false if unrecognized mouse button configuration:
- default: return false;
- }
- }
- }
- #endregion Camera Orientation & Pan Settings
- #region Mouse Pointer Locking Settings
- /// <summary>Controls whether or not the mouse pointer should be locked while panning.</summary>
- [Header("Mouse Lock Settings"), Tooltip("Should the mouse pointer be locked while panning?")]
- public bool lockWhilePanning = true;
- /// <summary>
- /// Controls whether or not the mouse pointer should be centered when released after being locked.
- /// <para>If false, only restores the mouse to its former position on windows.</para>
- /// </summary>
- [Tooltip("Should the mouse pointer be placed at the center of the game window when released after being locked?")]
- public bool centerMouseAfterLock = false;
- #endregion Mouse Pointer Locking Settings
- #region Camera Distance Raycast Settings
- /// <summary>Controls the distance the camera should be away from the surface of a raycast hit.</summary>
- [Header("Camera Distance Raycast Settings"), Tooltip("Controls the distance the camera should be away from the surface of a raycast hit.")]
- public float minSurfaceDistance = 0.3f;
- /// <summary>Controls which layers are valid for a raycast hit (Default: -1, Everything)</summary>
- [Tooltip("Controls which layers are valid for a raycast hit.")]
- public LayerMask raycastLayerMask = -1;
- /// <summary>If enabled, excludes the target's layer from the raycast at runtime.</summary>
- [Tooltip("Should the camera exclude the target's layer from the raycast automatically?")]
- public bool excludeTargetLayer = false;
- #endregion Camera Distance Raycast Settings
- #region Camera Controller Methods
- /// <summary>Updates the camera's position values given a delta of time.</summary>
- /// <param name="deltaTime">
- /// A parameter that represents how much time has passed since the last call.
- /// <para>Used to render the position the camera should be at immediately if 1.0f</para>
- /// </param>
- private void MoveCamera(float deltaTime) {
- // store the target's position (or Vector3.zero if no target is configured):
- Vector3 targetPosition = target ? target.position : Vector3.zero;
- // store the target's layer (or none if no target is configured):
- LayerMask targetLayer = target ? 1 << target.gameObject.layer : 0;
- // store the target's rotation (or a neutral rotation if no target is configured or if not configured to inherit the target's rotation):
- Quaternion targetRotation = inheritTargetRotation && target ? target.rotation : Quaternion.identity;
- // update the target offset:
- targetPosition += targetRotation * targetOffset;
- // normalize and constrain both the desired and true orientation's x and y axis:
- orientation = new Vector2(LoopAngle(orientation.x), ClampAngle(orientation.y, orientationYMin, orientationYMax));
- _orientation = new Vector2(LoopAngle(_orientation.x), ClampAngle(_orientation.y, orientationYMin, orientationYMax));
- // constrain both the desired and true distance:
- distance = Mathf.Clamp(distance, distanceMin, distanceMax);
- _distance = Mathf.Clamp(_distance, distanceMin, distanceMax);
- // store current distance for later during logic handling the movement after a raycast hits something:
- float prevDistance = _distance;
- // lerp the true orientation towards the desired orientation:
- _orientation = new Vector2(
- Mathf.LerpAngle(_orientation.x, orientation.x, Mathf.Clamp01(deltaTime * panLerpSpeed)),
- Mathf.LerpAngle(_orientation.y, orientation.y, Mathf.Clamp01(deltaTime * panLerpSpeed))
- );
- // lerp the true distance towards the desired distance:
- _distance = Mathf.Lerp(_distance, distance, Mathf.Clamp01(deltaTime * zoomLerpSpeed));
- // store the desired orientation as a quaternion:
- Quaternion rotation = Quaternion.Euler(_orientation.y, _orientation.x, 0);
- // create a ray that is pointing back and away from the target:
- Vector3 ray = targetRotation * rotation * Vector3.back;
- // prepare to capture a raycast hit:
- RaycastHit hit;
- // store the layermask of what counts as a valid raycast hit (and exclude the target's layer if exclude target layer is configured):
- LayerMask layerMask = raycastLayerMask & (excludeTargetLayer ? ~targetLayer : -1);
- // spherecast (which is a raycast but with a minimum surface distance) away from the target along the ray
- // to prevent the target from being obstructed and the camera from clipping into a surface:
- if (Physics.SphereCast(targetPosition, minSurfaceDistance, ray, out hit, distance, layerMask)) {
- // recalculate the lerping of the true distance towards the desired distance, this time accounting for the raycast hit:
- _distance = Mathf.Lerp(prevDistance, hit.distance, Mathf.Clamp01(deltaTime * zoomLerpClipSpeed));
- }
- // update the camera positoin and rotation:
- transform.position = targetPosition + (ray * _distance);
- transform.rotation = targetRotation * rotation;
- //// draw debug rays to show the desired distance (red) and the true distance (white) of the camera in the scene view:
- //Debug.DrawRay(targetPosition, ray * distance, Color.red);
- //Debug.DrawRay(targetPosition, ray * _distance, Color.white);
- }
- /// <summary>
- /// Called by Unity when the component values in the inspector are changed.
- /// <para>Used to reposition the Camera while editing the scene.</para>
- /// </summary>
- private void OnValidate() {
- // force the true orientation and distance to their desired values:
- _orientation = orientation;
- _distance = distance;
- // immediately move the camera to where it should be without lerping:
- MoveCamera(1f);
- }
- /// <summary>Awake is called once when script instance is being loaded. See Unity3D docs.</summary>
- private void Awake() {
- // setup the true orientation and distance:
- _orientation = orientation;
- _distance = distance;
- }
- /// <summary>LateUpdate is called every frame, if the component is enabled. See Unity3D docs.</summary>
- private void LateUpdate() {
- // store true distance and orientation for delta checking:
- float prevDistance = _distance;
- Vector2 prevOrientation = _orientation;
- // if we should lock the mouse cursor while panning:
- if (lockWhilePanning) {
- // if the mouse shouldn't be centered after the pan is complete:
- if (!centerMouseAfterLock) {
- // using the MousePointer class means the mouse position will be restored on release:
- MousePointer.lockState = IsMouseButtonHeld ? CursorLockMode.Locked : CursorLockMode.None;
- MousePointer.visible = !IsMouseButtonHeld;
- } else {
- // using the default Unity3D cursor class means the mouse will be centered on release:
- Cursor.lockState = IsMouseButtonHeld ? CursorLockMode.Locked : CursorLockMode.None;
- Cursor.visible = !IsMouseButtonHeld;
- }
- }
- // if the configured mouse button combination is pressed, then pan the camera's desired orientation by the mouse's movement deltas:
- if (IsMouseButtonHeld) {
- orientation += new Vector2(
- Input.GetAxis("Mouse X") * panSpeed.x,
- -Input.GetAxis("Mouse Y") * panSpeed.y) * Time.deltaTime;
- }
- // change the camera's desired distance by the mouses's scrollwheel delta:
- distance -= Input.GetAxis("Mouse ScrollWheel") * zoomSpeed;
- // move the camera's real orientation and distance towards their desired values given the delta of time since the last frame:
- MoveCamera(Time.deltaTime);
- }
- /// <summary>A helper function that takes an angle and returns what it would be if it was looped between 0 and 360.</summary>
- private static float LoopAngle(float angle) {
- return Mathf.Repeat(angle, 360);
- }
- /// <summary>A helper function that takes an angle and returns it clamped between a minimum and maximum.</summary>
- private static float ClampAngle(float angle, float min, float max) {
- if (angle < -360F) angle += 360F;
- if (angle > 360F) angle -= 360F;
- return Mathf.Clamp(angle, min, max);
- }
- #endregion Camera Controller Methods
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement