using UnityEngine; using UnityEngine.UI; using System.Collections; using System; using MoreMountains.Tools; using MoreMountains.Feedbacks; #if UNITY_EDITOR using UnityEditor; #endif namespace MoreMountains.Tools { /// /// Add this class to a camera and you'll be able to pilot it using the horizontal/vertical axis, and up/down controls set via its inspector. /// It's got an activation button, a run button, and an option to slow down time (this will require a MMTimeManager present in the scene) /// [AddComponentMenu("More Mountains/Tools/Camera/MMGhostCamera")] public class MMGhostCamera : MonoBehaviour { [Header("Speed")] /// the camera's movement speed public float MovementSpeed = 10f; /// the factor by which to multiply the speed when "running" public float RunFactor = 4f; /// the movement's acceleration public float Acceleration = 5f; /// the movement's deceleration public float Deceleration = 5f; /// the speed at which the camera rotates public float RotationSpeed = 40f; [Header("Controls")] /// the button used to toggle the camera on/off public KeyCode ActivateButton = KeyCode.RightShift; /// the name of the InputManager's horizontal axis public string HorizontalAxisName = "Horizontal"; /// the name of the InputManager's vertical axis public string VerticalAxisName = "Vertical"; /// the button to use to go up public KeyCode UpButton = KeyCode.Space; /// the button to use to go down public KeyCode DownButton = KeyCode.C; /// the button to use to switch between mobile and desktop control mode public KeyCode ControlsModeSwitch = KeyCode.M; /// the button used to modify the timescale public KeyCode TimescaleModificationButton = KeyCode.F; /// the button used to run while it's pressed public KeyCode RunButton = KeyCode.LeftShift; /// the mouse's sensitivity public float MouseSensitivity = 0.02f; /// the right stick sensitivity public float MobileStickSensitivity = 2f; [Header("Timescale Modification")] /// the amount to modify the timescale by when pressing the timescale button public float TimescaleModifier = 0.5f; [Header("Settings")] /// whether or not this camera should activate on start public bool AutoActivation = true; /// whether or not movement (up/down/left/right/forward/backward) is enabled public bool MovementEnabled = true; // whether or not rotation is enabled public bool RotationEnabled = true; [MMReadOnly] /// whether this camera is active or not right now public bool Active = false; [MMReadOnly] /// whether time is being altered right now or not public bool TimeAltered = false; [Header("Virtual Joysticks")] public bool UseMobileControls; [MMCondition("UseMobileControls", true)] public GameObject LeftStickContainer; [MMCondition("UseMobileControls", true)] public GameObject RightStickContainer; [MMCondition("UseMobileControls", true)] public MMTouchJoystick LeftStick; [MMCondition("UseMobileControls", true)] public MMTouchJoystick RightStick; protected Vector3 _currentInput; protected Vector3 _lerpedInput; protected Vector3 _normalizedInput; protected float _acceleration; protected float _deceleration; protected Vector3 _movementVector = Vector3.zero; protected float _speedMultiplier; protected Vector3 _newEulerAngles; /// /// On start, activate our camera if needed /// protected virtual void Start() { if (AutoActivation) { ToggleFreeCamera(); } } /// /// On Update we grab our input and move accordingly /// protected virtual void Update() { if (Input.GetKeyDown(ActivateButton)) { ToggleFreeCamera(); } if (!Active) { return; } GetInput(); Translate(); Rotate(); Move(); HandleMobileControls(); } /// /// Grabs and stores the various input values /// protected virtual void GetInput() { if (!UseMobileControls || (LeftStick == null)) { _currentInput.x = Input.GetAxis("Horizontal"); _currentInput.y = 0f; _currentInput.z = Input.GetAxis("Vertical"); } else { _currentInput.x = LeftStick._joystickValue.x; _currentInput.y = 0f; _currentInput.z = LeftStick._joystickValue.y; } if (Input.GetKey(UpButton)) { _currentInput.y = 1f; } if (Input.GetKey(DownButton)) { _currentInput.y = -1f; } _speedMultiplier = Input.GetKey(RunButton) ? RunFactor : 1f; _normalizedInput = _currentInput.normalized; if (Input.GetKeyDown(TimescaleModificationButton)) { ToggleSlowMotion(); } } /// /// Turns controls to mobile if needed /// protected virtual void HandleMobileControls() { if (Input.GetKeyDown(ControlsModeSwitch)) { UseMobileControls = !UseMobileControls; } if (UseMobileControls) { Cursor.lockState = CursorLockMode.None; Cursor.visible = true; } else if (Active) { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } if (LeftStickContainer != null) { LeftStickContainer?.SetActive(UseMobileControls); } if (RightStickContainer != null) { RightStickContainer?.SetActive(UseMobileControls); } } /// /// Computes the new position /// protected virtual void Translate() { if (!MovementEnabled) { return; } if ((Acceleration == 0) || (Deceleration == 0)) { _lerpedInput = _currentInput; } else { if (_normalizedInput.magnitude == 0) { _acceleration = Mathf.Lerp(_acceleration, 0f, Deceleration * Time.deltaTime); _lerpedInput = Vector3.Lerp(_lerpedInput, _lerpedInput * _acceleration, Time.deltaTime * Deceleration); } else { _acceleration = Mathf.Lerp(_acceleration, 1f, Acceleration * Time.deltaTime); _lerpedInput = Vector3.ClampMagnitude(_normalizedInput, _acceleration); } } _movementVector = _lerpedInput; _movementVector *= MovementSpeed * _speedMultiplier; if (_movementVector.magnitude > MovementSpeed * _speedMultiplier) { _movementVector = Vector3.ClampMagnitude(_movementVector, MovementSpeed * _speedMultiplier); } } /// /// Computes the new rotation /// protected virtual void Rotate() { if (!RotationEnabled) { return; } _newEulerAngles = this.transform.eulerAngles; if (!UseMobileControls || (LeftStick == null)) { _newEulerAngles.x += -Input.GetAxis("Mouse Y") * 359f * MouseSensitivity; _newEulerAngles.y += Input.GetAxis("Mouse X") * 359f * MouseSensitivity; } else { _newEulerAngles.x += -RightStick._joystickValue.y * MobileStickSensitivity; _newEulerAngles.y += RightStick._joystickValue.x * MobileStickSensitivity; } _newEulerAngles = Vector3.Lerp(this.transform.eulerAngles, _newEulerAngles, Time.deltaTime * RotationSpeed); } /// /// Modifies the camera's transform's position and rotation /// protected virtual void Move() { transform.eulerAngles = _newEulerAngles; transform.position += transform.rotation * _movementVector * Time.deltaTime; } /// /// Toggles the timescale modification /// protected virtual void ToggleSlowMotion() { TimeAltered = !TimeAltered; if (TimeAltered) { MMTimeScaleEvent.Trigger(MMTimeScaleMethods.For, TimescaleModifier, 1f, true, 5f, true); } else { MMTimeScaleEvent.Trigger(MMTimeScaleMethods.Unfreeze, 1f, 0f, false, 0f, false); } } /// /// Toggles the camera's active state /// protected virtual void ToggleFreeCamera() { Active = !Active; Cursor.lockState = Active ? CursorLockMode.Locked : CursorLockMode.None; Cursor.visible = !Active; } } }