using System.Runtime.CompilerServices; using UnityEngine.InputSystem.LowLevel; using UnityEngine.Scripting; ////REVIEW: introduce separate base class for ButtonControl and AxisControl instead of deriving ButtonControl from AxisControl? namespace UnityEngine.InputSystem.Controls { /// /// An axis that has a trigger point beyond which it is considered to be pressed. /// /// /// By default stored as a single bit. In that format, buttons will only yield 0 /// and 1 as values. However, buttons return are s and /// yield full floating-point values and may thus have a range of values. See /// for how button presses on such buttons are handled. /// public class ButtonControl : AxisControl { private bool m_NeedsToCheckFramePress = false; private uint m_UpdateCountLastPressed = uint.MaxValue; private uint m_UpdateCountLastReleased = uint.MaxValue; private bool m_LastUpdateWasPress; #if UNITY_EDITOR // Editor input updates have a separate block of state memory, so must be checked separately private uint m_UpdateCountLastPressedEditor = uint.MaxValue; private uint m_UpdateCountLastReleasedEditor = uint.MaxValue; private bool m_LastUpdateWasPressEditor; #endif internal bool needsToCheckFramePress { get; private set; } ////REVIEW: are per-control press points really necessary? can we just drop them? /// /// The minimum value the button has to reach for it to be considered pressed. /// /// Button press threshold. /// /// The button is considered pressed, if it has a value equal to or greater than /// this value. /// /// By default, this property is set to -1. If the value of the property is negative, /// is used. /// /// The value can be configured as a parameter in a layout. /// /// /// /// public class MyDevice : InputDevice /// { /// [InputControl(parameters = "pressPoint=0.234")] /// public ButtonControl button { get; private set; } /// /// //... /// } /// /// /// /// /// /// public float pressPoint = -1; /// /// Return if set, otherwise return . /// /// Effective value to use for press point thresholds. /// public float pressPointOrDefault => pressPoint > 0 ? pressPoint : s_GlobalDefaultButtonPressPoint; /// /// Default-initialize the control. /// /// /// The default format for the control is . /// The control's minimum value is set to 0 and the maximum value to 1. /// public ButtonControl() { m_StateBlock.format = InputStateBlock.FormatBit; m_MinValue = 0f; m_MaxValue = 1f; } /// /// Whether the given value would be considered pressed for this button. /// /// Value for the button. /// True if crosses the threshold to be considered pressed. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public new bool IsValueConsideredPressed(float value) { return value >= pressPointOrDefault; } /// /// Whether the button is currently pressed. /// /// True if button is currently pressed. /// /// A button is considered pressed if its value is equal to or greater /// than its button press threshold (). /// /// /// You can use this to read whether specific keys are currently pressed by using isPressed on keys, as shown in the following examples: /// /// /// /// Note: The Input System identifies keys by physical layout, not according to the current language mapping of the keyboard. To query the name of the key according to the language mapping, use . /// /// You can also use this to read mouse buttons, as shown in the following examples: /// /// /// /// You can also check through all numbered buttons on the mouse: (this example does not cause allocations) /// /// /// /// Or you can look up controls by name, like this: /// /// /// /// /// /// /// public bool isPressed { get { // Take the old path if we don't have the speed gain from already testing wasPressedThisFrame/wasReleasedThisFrame. if (!needsToCheckFramePress) return IsValueConsideredPressed(value); #if UNITY_EDITOR if (InputUpdate.s_LatestUpdateType.IsEditorUpdate()) return m_LastUpdateWasPressEditor; #endif return m_LastUpdateWasPress; } } // When we start caring about inter-frame presses, use the info we have to set up the alternate path. // If we don't do this, users could call wasPressedThisFrame/wasReleasedThisFrame twice for the first time in // a single frame, and the returned value may be incorrect until the next frame. private void BeginTestingForFramePresses(bool currentlyPressed, bool pressedLastFrame) { needsToCheckFramePress = true; device.m_ButtonControlsCheckingPressState.Add(this); #if UNITY_EDITOR if (InputUpdate.s_LatestUpdateType.IsEditorUpdate()) { m_LastUpdateWasPressEditor = currentlyPressed; if (currentlyPressed && !pressedLastFrame) m_UpdateCountLastPressedEditor = device.m_CurrentUpdateStepCount; else if (pressedLastFrame && !currentlyPressed) m_UpdateCountLastReleasedEditor = device.m_CurrentUpdateStepCount; } else #endif { m_LastUpdateWasPress = currentlyPressed; if (currentlyPressed && !pressedLastFrame) m_UpdateCountLastPressed = device.m_CurrentUpdateStepCount; else if (pressedLastFrame && !currentlyPressed) m_UpdateCountLastReleased = device.m_CurrentUpdateStepCount; } } /// /// Whether the press started this frame. /// /// True if the current press of the button started this frame. /// /// The first time this function - or wasReleasedThisFrame - are called, it's possible that extremely fast /// inputs (or very slow frame update times) will result in presses/releases being missed. /// Following the next input system update after either have been called, and from then on until the device is /// destroyed, this ceases to be an issue. /// /// /// // An example showing the use of this property on a gamepad button and a keyboard key. /// /// using UnityEngine; /// using UnityEngine.InputSystem; /// /// public class ExampleScript : MonoBehaviour /// { /// void Update() /// { /// bool buttonPressed = Gamepad.current.aButton.wasPressedThisFrame; /// bool spaceKeyPressed = Keyboard.current.spaceKey.wasPressedThisFrame; /// } /// } /// /// /// _Note_: The Input System identifies keys by physical layout, not according to the current language mapping of the keyboard. To query the name of the key according to the language mapping, use . /// /// You can also use this property to read mouse buttons. For example: /// /// /// /// Mouse.current.leftButton.wasPressedThisFrame /// Mouse.current.rightButton.wasPressedThisFrame /// Mouse.current.middleButton.wasPressedThisFrame /// /// /// /// /// public bool wasPressedThisFrame { get { // Take the old path if this is the first time calling. if (!needsToCheckFramePress) { var currentlyPressed = IsValueConsideredPressed(value); var pressedLastFrame = IsValueConsideredPressed(ReadValueFromPreviousFrame()); BeginTestingForFramePresses(currentlyPressed, pressedLastFrame); return device.wasUpdatedThisFrame && currentlyPressed && !pressedLastFrame; } #if UNITY_EDITOR if (InputUpdate.s_LatestUpdateType.IsEditorUpdate()) return InputUpdate.s_UpdateStepCount == m_UpdateCountLastPressedEditor; #endif return InputUpdate.s_UpdateStepCount == m_UpdateCountLastPressed; } } /// /// Whether the press ended this frame. /// /// True if the current press of the button ended this frame. /// /// _Note_: The Input System identifies keys by physical layout, not according to the current language mapping of the keyboard. To query the name of the key according to the language mapping, use . /// /// /// An example showing the use of this property on a gamepad button and a keyboard key: /// /// using UnityEngine; /// using UnityEngine.InputSystem; /// /// public class ExampleScript : MonoBehaviour /// { /// void Update() /// { /// bool buttonPressed = Gamepad.current.aButton.wasReleasedThisFrame; /// bool spaceKeyPressed = Keyboard.current.spaceKey.wasReleasedThisFrame; /// } /// } /// /// public bool wasReleasedThisFrame { get { // Take the old path if this is the first time calling. if (!needsToCheckFramePress) { var currentlyPressed = IsValueConsideredPressed(value); var pressedLastFrame = IsValueConsideredPressed(ReadValueFromPreviousFrame()); BeginTestingForFramePresses(currentlyPressed, pressedLastFrame); return device.wasUpdatedThisFrame && !currentlyPressed && pressedLastFrame; } #if UNITY_EDITOR if (InputUpdate.s_LatestUpdateType.IsEditorUpdate()) return InputUpdate.s_UpdateStepCount == m_UpdateCountLastReleasedEditor; #endif return InputUpdate.s_UpdateStepCount == m_UpdateCountLastReleased; } } internal void UpdateWasPressed() { var isNowPressed = IsValueConsideredPressed(value); if (m_LastUpdateWasPress != isNowPressed) { if (isNowPressed) m_UpdateCountLastPressed = device.m_CurrentUpdateStepCount; else m_UpdateCountLastReleased = device.m_CurrentUpdateStepCount; m_LastUpdateWasPress = isNowPressed; } } #if UNITY_EDITOR internal void UpdateWasPressedEditor() { var isNowPressed = IsValueConsideredPressed(value); if (m_LastUpdateWasPressEditor != isNowPressed) { if (isNowPressed) m_UpdateCountLastPressedEditor = device.m_CurrentUpdateStepCount; else m_UpdateCountLastReleasedEditor = device.m_CurrentUpdateStepCount; m_LastUpdateWasPressEditor = isNowPressed; } } #endif // UNITY_EDITOR // We make the current global default button press point available as a static so that we don't have to // constantly make the hop from InputSystem.settings -> InputManager.m_Settings -> defaultButtonPressPoint. internal static float s_GlobalDefaultButtonPressPoint; internal static float s_GlobalDefaultButtonReleaseThreshold; // We clamp button press points to this value as allowing 0 as the press point causes all buttons // to implicitly be pressed all the time. Not useful. internal const float kMinButtonPressPoint = 0.0001f; } }