/// --------------------------------------------- /// Ultimate Character Controller /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.UltimateCharacterController.Input { using Opsive.UltimateCharacterController.Input.VirtualControls; using System.Collections.Generic; using UnityEngine; /// /// Acts as a common base class for input using the Unity Input Manager. Works with keyboard/mouse, controller, and mobile input. /// public class UnityInput : PlayerInput { /// /// Specifies if any input type should be forced. /// public enum ForceInputType { None, Standalone, Virtual } [Tooltip("Specifies if any input type should be forced.")] [SerializeField] protected ForceInputType m_ForceInput; [Tooltip("Should the cursor be disabled?")] [SerializeField] protected bool m_DisableCursor = true; [Tooltip("Should the cursor be enabled when the escape key is pressed?")] [SerializeField] protected bool m_EnableCursorWithEscape = true; [Tooltip("If the cursor is enabled with escape should the look vector be prevented from updating?")] [SerializeField] protected bool m_PreventLookVectorChanges = true; [Tooltip("The joystick is considered up when the raw value is less than the specified threshold.")] [Range(0, 1)] [SerializeField] protected float m_JoystickUpThreshold = 1; public bool DisableCursor { get { return m_DisableCursor; } set { if (m_Input is VirtualInput) { m_DisableCursor = false; } m_DisableCursor = value; if (m_DisableCursor && Cursor.visible) { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } else if (!m_DisableCursor && !Cursor.visible) { Cursor.lockState = CursorLockMode.None; Cursor.visible = true; } } } public bool EnableCursorWithEscape { get { return m_EnableCursorWithEscape; } set { m_EnableCursorWithEscape = value; } } public bool PreventLookMovementWithEscape { get { return m_PreventLookVectorChanges; } set { m_PreventLookVectorChanges = value; } } public float JoystickUpThreshold { get { return m_JoystickUpThreshold; } set { m_JoystickUpThreshold = value; } } private InputBase m_Input; private bool m_UseVirtualInput; private HashSet m_JoystickDownSet; private HashSet m_ToAddJoystickDownSet; private HashSet m_ToRemoveJoystickDownSet; /// /// Initialize the default values. /// protected override void Awake() { base.Awake(); m_UseVirtualInput = m_ForceInput == ForceInputType.Virtual; #if !UNITY_EDITOR && (UNITY_IPHONE || UNITY_ANDROID || UNITY_WP_8_1 || UNITY_BLACKBERRY) if (m_ForceInput != ForceInputType.Standalone) { m_UseVirtualInput = true; } #endif if (m_UseVirtualInput) { m_Input = new VirtualInput(); // The cursor must be enabled for virtual controls to allow the drag events to occur. m_DisableCursor = false; } else { m_Input = new StandaloneInput(); } m_Input.Initialize(this); } /// /// The component has been enabled. /// private void OnEnable() { if (!m_UseVirtualInput && m_DisableCursor) { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } } /// /// Associates the VirtualControlsManager with the VirtualInput object. /// /// The VirtualControlsManager to associate with the VirtualInput object. /// True if the virtual controls were registered. public bool RegisterVirtualControlsManager(VirtualControlsManager virtualControlsManager) { if (m_Input is VirtualInput) { (m_Input as VirtualInput).RegisterVirtualControlsManager(virtualControlsManager); return true; } return false; } /// /// Removes the VirtualControlsManager association. /// public void UnegisterVirtualControlsManager() { if (m_Input is VirtualInput) { (m_Input as VirtualInput).UnregisterVirtualControlsManager(); } } /// /// Update the joystick and cursor state values. /// private void LateUpdate() { if (m_UseVirtualInput) { return; } // The joystick is no longer down after the axis is 0. if (IsControllerConnected()) { // GetButtonUp/Down doesn't immediately add the button name to the set to prevent the GetButtonUp/Down from returning false // if it is called twice within the same frame. Add it after the frame has ended. if (m_JoystickDownSet != null) { foreach (var item in m_JoystickDownSet) { if (Mathf.Abs(m_Input.GetAxisRaw(item)) < m_JoystickUpThreshold) { m_ToRemoveJoystickDownSet.Add(item); } } foreach (var item in m_ToRemoveJoystickDownSet) { m_JoystickDownSet.Remove(item); } m_ToRemoveJoystickDownSet.Clear(); } if (m_ToAddJoystickDownSet != null && m_ToAddJoystickDownSet.Count > 0) { if (m_JoystickDownSet == null) { m_JoystickDownSet = new HashSet(); m_ToRemoveJoystickDownSet = new HashSet(); } foreach (var item in m_ToAddJoystickDownSet) { m_JoystickDownSet.Add(item); } m_ToAddJoystickDownSet.Clear(); } } // Enable the cursor if the escape key is pressed. Disable the cursor if it is visbile but should be disabled upon press. if (m_EnableCursorWithEscape && UnityEngine.Input.GetKeyDown(KeyCode.Escape)) { Cursor.lockState = CursorLockMode.None; Cursor.visible = true; if (m_PreventLookVectorChanges) { OnApplicationFocus(false); } } else if (Cursor.visible && m_DisableCursor && !IsPointerOverUI() && (UnityEngine.Input.GetKeyDown(KeyCode.Mouse0) || UnityEngine.Input.GetKeyDown(KeyCode.Mouse1))) { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; if (m_PreventLookVectorChanges) { OnApplicationFocus(true); } } #if UNITY_EDITOR // The cursor should be visible when the game is paused. if (!Cursor.visible && Time.deltaTime == 0 && !m_DisableCursor) { Cursor.lockState = CursorLockMode.None; Cursor.visible = true; } #endif } /// /// Internal method which returns true if the button is being pressed. /// /// The name of the button. /// True of the button is being pressed. protected override bool GetButtonInternal(string name) { if (m_Input.GetButton(name, InputBase.ButtonAction.GetButton)) { return true; } if (IsControllerConnected()) { if (Mathf.Abs(m_Input.GetAxisRaw(name)) == 1) { if (m_JoystickDownSet == null || !m_JoystickDownSet.Contains(name)) { if (m_ToAddJoystickDownSet == null) { m_ToAddJoystickDownSet = new HashSet(); } m_ToAddJoystickDownSet.Add(name); } return true; } } return false; } /// /// Internal method which returns true if the button was pressed this frame. /// /// The name of the button. /// True if the button is pressed this frame. protected override bool GetButtonDownInternal(string name) { if (IsControllerConnected() && Mathf.Abs(m_Input.GetAxisRaw(name)) == 1) { // The button should only be considered down on the first frame. if (m_JoystickDownSet != null && m_JoystickDownSet.Contains(name)) { return false; } if (m_ToAddJoystickDownSet == null) { m_ToAddJoystickDownSet = new HashSet(); } m_ToAddJoystickDownSet.Add(name); return true; } return m_Input.GetButton(name, InputBase.ButtonAction.GetButtonDown); } /// /// Internal method which returnstrue if the button is up. /// /// The name of the button. /// True if the button is up. protected override bool GetButtonUpInternal(string name) { if (IsControllerConnected()) { if (Mathf.Abs(m_Input.GetAxisRaw(name)) == 1 && (m_JoystickDownSet == null || !m_JoystickDownSet.Contains(name))) { if (m_ToAddJoystickDownSet == null) { m_ToAddJoystickDownSet = new HashSet(); } m_ToAddJoystickDownSet.Add(name); return false; } if (m_JoystickDownSet != null && m_JoystickDownSet.Contains(name) && m_Input.GetAxisRaw(name) < m_JoystickUpThreshold) { return true; } return false; } return m_Input.GetButton(name, InputBase.ButtonAction.GetButtonUp); } /// /// Internal method which returns the value of the axis with the specified name. /// /// The name of the axis. /// The value of the axis. protected override float GetAxisInternal(string name) { return m_Input.GetAxis(name); } /// /// Internal method which returns the value of the raw axis with the specified name. /// /// The name of the axis. /// The value of the raw axis. protected override float GetAxisRawInternal(string name) { return m_Input.GetAxisRaw(name); } /// /// Returns true if the pointer is over a UI element. /// /// True if the pointer is over a UI element. public override bool IsPointerOverUI() { // The input will always be over a UI element with virtual inputs. if (m_Input is VirtualInput) { return false; } return base.IsPointerOverUI(); } /// /// Enables or disables gameplay input. An example of when it will not be enabled is when there is a fullscreen UI over the main camera. /// /// True if the input is enabled. protected override void EnableGameplayInput(bool enable) { base.EnableGameplayInput(enable); if (enable && m_DisableCursor) { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } } /// /// Does the game have focus? /// /// True if the game has focus. protected override void OnApplicationFocus(bool hasFocus) { base.OnApplicationFocus(hasFocus); if (enabled && hasFocus && m_DisableCursor) { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } } } }