123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- using System;
- using UnityEngine.InputSystem.LowLevel;
-
- namespace UnityEngine.InputSystem
- {
- /// <summary>
- /// Information passed to <see cref="IInputInteraction">interactions</see>
- /// when their associated controls trigger.
- /// </summary>
- /// <seealso cref="IInputInteraction.Process"/>
- public struct InputInteractionContext
- {
- /// <summary>
- /// The action associated with the binding.
- /// </summary>
- /// <remarks>
- /// If the binding is not associated with an action, this is <c>null</c>.
- /// </remarks>
- /// <seealso cref="InputBinding.action"/>
- public InputAction action => m_State.GetActionOrNull(ref m_TriggerState);
-
- /// <summary>
- /// The bound control that changed its state to trigger the binding associated
- /// with the interaction.
- /// </summary>
- /// <remarks>
- /// In case the binding associated with the interaction is a composite, this is
- /// one of the controls that are part of the composite.
- /// </remarks>
- /// <seealso cref="InputBinding.path"/>
- public InputControl control => m_State.GetControl(ref m_TriggerState);
-
- /// <summary>
- /// The phase the interaction is currently in.
- /// </summary>
- /// <remarks>
- /// Each interaction on a binding has its own phase independent of the action the binding is applied to.
- /// If an interaction gets to "drive" an action at a particular point in time, its phase will determine
- /// the phase of the action.
- /// </remarks>
- /// <seealso cref="InputAction.phase"/>
- /// <seealso cref="Started"/>
- /// <seealso cref="Waiting"/>
- /// <seealso cref="Performed"/>
- /// <seealso cref="Canceled"/>
- public InputActionPhase phase => m_TriggerState.phase;
-
- /// <summary>
- /// Time stamp of the input event that caused <see cref="control"/> to trigger a change in the
- /// state of <see cref="action"/>.
- /// </summary>
- /// <seealso cref="InputEvent.time"/>
- public double time => m_TriggerState.time;
-
- /// <summary>
- /// Timestamp of the <see cref="InputEvent"/> that caused the interaction to transition
- /// to <see cref="InputActionPhase.Started"/>.
- /// </summary>
- /// <seealso cref="InputEvent.time"/>
- public double startTime => m_TriggerState.startTime;
-
- /// <summary>
- /// Whether the interaction's <see cref="IInputInteraction.Process"/> method has been called because
- /// a timer set by <see cref="SetTimeout"/> has expired.
- /// </summary>
- /// <seealso cref="SetTimeout"/>
- public bool timerHasExpired
- {
- get => (m_Flags & Flags.TimerHasExpired) != 0;
- internal set
- {
- if (value)
- m_Flags |= Flags.TimerHasExpired;
- else
- m_Flags &= ~Flags.TimerHasExpired;
- }
- }
-
- /// <summary>
- /// True if the interaction is waiting for input
- /// </summary>
- /// <remarks>
- /// By default, an interaction will return this this phase after every time it has been performed
- /// (<see cref="InputActionPhase.Performed"/>). This can be changed by using <see cref="PerformedAndStayStarted"/>
- /// or <see cref="PerformedAndStayPerformed"/>.
- /// </remarks>
- /// <seealso cref="InputActionPhase.Waiting"/>
- public bool isWaiting => phase == InputActionPhase.Waiting;
-
- /// <summary>
- /// True if the interaction has been started.
- /// </summary>
- /// <seealso cref="InputActionPhase.Started"/>
- /// <seealso cref="Started"/>
- public bool isStarted => phase == InputActionPhase.Started;
-
- /// <summary>
- /// Compute the current level of control actuation.
- /// </summary>
- /// <returns>The current level of control actuation (usually [0..1]) or -1 if the control is actuated
- /// but does not support computing magnitudes.</returns>
- /// <seealso cref="ControlIsActuated"/>
- /// <seealso cref="InputControl.EvaluateMagnitude()"/>
- public float ComputeMagnitude()
- {
- return m_TriggerState.magnitude;
- }
-
- /// <summary>
- /// Return true if the control that triggered the interaction has been actuated beyond the given threshold.
- /// </summary>
- /// <param name="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.</param>
- /// <returns>True if the trigger control is actuated.</returns>
- /// <seealso cref="InputControlExtensions.IsActuated"/>
- /// <seealso cref="ComputeMagnitude"/>
- public bool ControlIsActuated(float threshold = 0)
- {
- return InputActionState.IsActuated(ref m_TriggerState, threshold);
- }
-
- /// <summary>
- /// Mark the interaction has having begun.
- /// </summary>
- /// <remarks>
- /// 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.
- ///
- /// <example>
- /// <code>
- /// 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.
- /// }
- /// }
- /// </code>
- /// </example>
- /// </remarks>
- 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);
- }
-
- /// <summary>
- /// Put the interaction back into <see cref="InputActionPhase.Waiting"/> state.
- /// </summary>
- /// <seealso cref="InputAction.phase"/>
- /// <seealso cref="InputActionPhase"/>
- /// <seealso cref="Started"/>
- /// <seealso cref="Performed"/>
- /// <seealso cref="Canceled"/>
- public void Waiting()
- {
- if (m_TriggerState.phase != InputActionPhase.Waiting)
- m_State.ChangePhaseOfInteraction(InputActionPhase.Waiting, ref m_TriggerState);
- }
-
- /// <summary>
- /// Start a timeout that triggers within <paramref name="seconds"/>.
- /// </summary>
- /// <param name="seconds">Number of seconds before the timeout is triggered.</param>
- /// <remarks>
- /// An interaction might wait a set amount of time for something to happen and then
- /// do something depending on whether it did or did not happen. By calling this method,
- /// a timeout is installed such that in the input update that the timer expires in, the
- /// interaction's <see cref="IInputInteraction.Process"/> method is called with <see cref="timerHasExpired"/>
- /// being true.
- ///
- /// Changing the phase of the interaction while a timeout is running will implicitly cancel
- /// the timeout.
- ///
- /// <example>
- /// <code>
- /// // Let's say we're writing a Process() method for an interaction that,
- /// // after a control has been actuated, waits for 1 second for it to be
- /// // released again. If that happens, the interaction performs. If not,
- /// // it cancels.
- /// public void Process(ref InputInteractionContext context)
- /// {
- /// // timerHasExpired will be true if we get called when our timeout
- /// // has expired.
- /// if (context.timerHasExpired)
- /// {
- /// // The user did not release the control quickly enough.
- /// // Our interaction is not successful, so cancel.
- /// context.Canceled();
- /// return;
- /// }
- ///
- /// if (context.ControlIsActuated())
- /// {
- /// if (!context.isStarted)
- /// {
- /// // The control has been actuated. We want to give the user a max
- /// // of 1 second to release it. So we start the interaction now and then
- /// // set the timeout.
- /// context.Started();
- /// context.SetTimeout(1);
- /// }
- /// }
- /// else
- /// {
- /// // Control has been released. If we're currently waiting for a release,
- /// // it has come in time before out timeout expired. In other words, the
- /// // interaction has been successfully performed. We call Performed()
- /// // which implicitly removes our ongoing timeout.
- /// if (context.isStarted)
- /// context.Performed();
- /// }
- /// }
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="timerHasExpired"/>
- public void SetTimeout(float seconds)
- {
- m_State.StartTimeout(seconds, ref m_TriggerState);
- }
-
- /// <summary>
- /// Override the default timeout value used by <see cref="InputAction.GetTimeoutCompletionPercentage"/>.
- /// </summary>
- /// <param name="seconds">Amount of total successive timeouts TODO</param>
- /// <exception cref="ArgumentException"></exception>
- /// <remarks>
- /// By default, timeout completion will be entirely determine by the timeout that is currently
- /// running, if any. However, some interactions (such as <see cref="Interactions.MultiTapInteraction"/>)
- /// will have to run multiple timeouts in succession. Thus, completion of a single timeout is not
- /// the same as completion of the interaction.
- ///
- /// You can use this method to account for this.
- ///
- /// Whenever a timeout completes, the timeout duration will automatically be accumulated towards
- /// the total timeout completion time.
- ///
- /// <example>
- /// <code>
- /// // Let's say we're starting our first timeout and we know that we will run three timeouts
- /// // in succession of 2 seconds each. By calling SetTotalTimeoutCompletionTime(), we can account for this.
- /// SetTotalTimeoutCompletionTime(3 * 2);
- ///
- /// // Start the first timeout. When this timeout expires, it will automatically
- /// // count one second towards the total timeout completion time.
- /// SetTimeout(2);
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="InputAction.GetTimeoutCompletionPercentage"/>
- public void SetTotalTimeoutCompletionTime(float seconds)
- {
- if (seconds <= 0)
- throw new ArgumentException("Seconds must be a positive value", nameof(seconds));
-
- m_State.SetTotalTimeoutCompletionTime(seconds, ref m_TriggerState);
- }
-
- /// <summary>
- /// Read the value of the binding that triggered processing of the interaction.
- /// </summary>
- /// <typeparam name="TValue">Type of value to read from the binding. Must match the value type of the control
- /// or composite in effect for the binding.</typeparam>
- /// <returns>Value read from the binding.</returns>
- public TValue ReadValue<TValue>()
- where TValue : struct
- {
- return m_State.ReadValue<TValue>(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
- }
- }
- }
|