using System; using UnityEngine.InputSystem.LowLevel; ////REVIEW: should timer expiration be a separate method on IInputInteraction? namespace UnityEngine.InputSystem { /// /// Information passed to interactions /// when their associated controls trigger. /// /// public struct InputInteractionContext { /// /// The action associated with the binding. /// /// /// If the binding is not associated with an action, this is null. /// /// public InputAction action => m_State.GetActionOrNull(ref m_TriggerState); /// /// The bound control that changed its state to trigger the binding associated /// with the interaction. /// /// /// In case the binding associated with the interaction is a composite, this is /// one of the controls that are part of the composite. /// /// public InputControl control => m_State.GetControl(ref m_TriggerState); public InputActionPhase phase => m_TriggerState.phase; /// /// Time stamp of the input event that caused to trigger a change in the /// state of . /// /// public double time => m_TriggerState.time; public double startTime => m_TriggerState.startTime; public bool timerHasExpired { get => (m_Flags & Flags.TimerHasExpired) != 0; internal set { if (value) m_Flags |= Flags.TimerHasExpired; else m_Flags &= ~Flags.TimerHasExpired; } } /// /// True if the interaction is waiting for input /// /// /// By default, an interaction will return this this phase after every time it has been performed /// (). This can be changed by using /// or . /// /// public bool isWaiting => phase == InputActionPhase.Waiting; /// /// True if the interaction has been started. /// /// /// public bool isStarted => phase == InputActionPhase.Started; /// /// Return true if the control that triggered the interaction has been actuated beyond the given threshold. /// /// Threshold that must be reached for the control to be considered actuated. If this is zero, /// the threshold must be exceeded. If it is any positive value, the value must be at least matched. /// True if the trigger control is actuated. /// public bool ControlIsActuated(float threshold = 0) { return m_State.IsActuated(ref m_TriggerState, threshold); } /// /// Mark the interaction has having begun. /// /// /// Note that this affects the current interaction only. There may be multiple interactions on a binding /// and arbitrary many interactions may concurrently be in started state. However, only one interaction /// (usually the one that starts first) is allowed to drive the action's state as a whole. If an interaction /// that is currently driving an action is canceled, however, the next interaction in the list that has /// been started will take over and continue driving the action. /// /// /// /// public class MyInteraction : IInputInteraction<float> /// { /// public void Process(ref IInputInteractionContext context) /// { /// if (context.isWaiting && context.ControlIsActuated()) /// { /// // We've waited for input and got it. Start the interaction. /// context.Started(); /// } /// else if (context.isStarted && !context.ControlIsActuated()) /// { /// // Interaction has been completed. /// context.Performed(); /// } /// } /// /// public void Reset() /// { /// // No reset code needed. We're not keeping any state locally in the interaction. /// } /// } /// /// /// public void Started() { m_TriggerState.startTime = time; m_State.ChangePhaseOfInteraction(InputActionPhase.Started, ref m_TriggerState); } public void Performed() { if (m_TriggerState.phase == InputActionPhase.Waiting) m_TriggerState.startTime = time; m_State.ChangePhaseOfInteraction(InputActionPhase.Performed, ref m_TriggerState); } public void PerformedAndStayStarted() { if (m_TriggerState.phase == InputActionPhase.Waiting) m_TriggerState.startTime = time; m_State.ChangePhaseOfInteraction(InputActionPhase.Performed, ref m_TriggerState, phaseAfterPerformed: InputActionPhase.Started); } public void PerformedAndStayPerformed() { if (m_TriggerState.phase == InputActionPhase.Waiting) m_TriggerState.startTime = time; m_State.ChangePhaseOfInteraction(InputActionPhase.Performed, ref m_TriggerState, phaseAfterPerformed: InputActionPhase.Performed); } public void Canceled() { if (m_TriggerState.phase != InputActionPhase.Canceled) m_State.ChangePhaseOfInteraction(InputActionPhase.Canceled, ref m_TriggerState); } public void Waiting() { if (m_TriggerState.phase != InputActionPhase.Waiting) m_State.ChangePhaseOfInteraction(InputActionPhase.Waiting, ref m_TriggerState); } public void SetTimeout(float seconds) { m_State.StartTimeout(seconds, ref m_TriggerState); } public TValue ReadValue() where TValue : struct { return m_State.ReadValue(m_TriggerState.bindingIndex, m_TriggerState.controlIndex); } internal InputActionState m_State; internal Flags m_Flags; internal InputActionState.TriggerState m_TriggerState; internal int mapIndex => m_TriggerState.mapIndex; internal int controlIndex => m_TriggerState.controlIndex; internal int bindingIndex => m_TriggerState.bindingIndex; internal int interactionIndex => m_TriggerState.interactionIndex; [Flags] internal enum Flags { TimerHasExpired = 1 << 1 } } }