InputInteractionContext.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. using System;
  2. using UnityEngine.InputSystem.LowLevel;
  3. ////REVIEW: should timer expiration be a separate method on IInputInteraction?
  4. namespace UnityEngine.InputSystem
  5. {
  6. /// <summary>
  7. /// Information passed to <see cref="IInputInteraction">interactions</see>
  8. /// when their associated controls trigger.
  9. /// </summary>
  10. /// <seealso cref="IInputInteraction.Process"/>
  11. public struct InputInteractionContext
  12. {
  13. /// <summary>
  14. /// The action associated with the binding.
  15. /// </summary>
  16. /// <remarks>
  17. /// If the binding is not associated with an action, this is <c>null</c>.
  18. /// </remarks>
  19. /// <seealso cref="InputBinding.action"/>
  20. public InputAction action => m_State.GetActionOrNull(ref m_TriggerState);
  21. /// <summary>
  22. /// The bound control that changed its state to trigger the binding associated
  23. /// with the interaction.
  24. /// </summary>
  25. /// <remarks>
  26. /// In case the binding associated with the interaction is a composite, this is
  27. /// one of the controls that are part of the composite.
  28. /// </remarks>
  29. /// <seealso cref="InputBinding.path"/>
  30. public InputControl control => m_State.GetControl(ref m_TriggerState);
  31. public InputActionPhase phase => m_TriggerState.phase;
  32. /// <summary>
  33. /// Time stamp of the input event that caused <see cref="control"/> to trigger a change in the
  34. /// state of <see cref="action"/>.
  35. /// </summary>
  36. /// <seealso cref="InputEvent.time"/>
  37. public double time => m_TriggerState.time;
  38. public double startTime => m_TriggerState.startTime;
  39. public bool timerHasExpired
  40. {
  41. get => (m_Flags & Flags.TimerHasExpired) != 0;
  42. internal set
  43. {
  44. if (value)
  45. m_Flags |= Flags.TimerHasExpired;
  46. else
  47. m_Flags &= ~Flags.TimerHasExpired;
  48. }
  49. }
  50. /// <summary>
  51. /// True if the interaction is waiting for input
  52. /// </summary>
  53. /// <remarks>
  54. /// By default, an interaction will return this this phase after every time it has been performed
  55. /// (<see cref="InputActionPhase.Performed"/>). This can be changed by using <see cref="PerformedAndStayStarted"/>
  56. /// or <see cref="PerformedAndStayPerformed"/>.
  57. /// </remarks>
  58. /// <seealso cref="InputActionPhase.Waiting"/>
  59. public bool isWaiting => phase == InputActionPhase.Waiting;
  60. /// <summary>
  61. /// True if the interaction has been started.
  62. /// </summary>
  63. /// <seealso cref="InputActionPhase.Started"/>
  64. /// <seealso cref="Started"/>
  65. public bool isStarted => phase == InputActionPhase.Started;
  66. /// <summary>
  67. /// Return true if the control that triggered the interaction has been actuated beyond the given threshold.
  68. /// </summary>
  69. /// <param name="threshold">Threshold that must be reached for the control to be considered actuated. If this is zero,
  70. /// the threshold must be exceeded. If it is any positive value, the value must be at least matched.</param>
  71. /// <returns>True if the trigger control is actuated.</returns>
  72. /// <seealso cref="InputControlExtensions.IsActuated"/>
  73. public bool ControlIsActuated(float threshold = 0)
  74. {
  75. return m_State.IsActuated(ref m_TriggerState, threshold);
  76. }
  77. /// <summary>
  78. /// Mark the interaction has having begun.
  79. /// </summary>
  80. /// <remarks>
  81. /// Note that this affects the current interaction only. There may be multiple interactions on a binding
  82. /// and arbitrary many interactions may concurrently be in started state. However, only one interaction
  83. /// (usually the one that starts first) is allowed to drive the action's state as a whole. If an interaction
  84. /// that is currently driving an action is canceled, however, the next interaction in the list that has
  85. /// been started will take over and continue driving the action.
  86. ///
  87. /// <example>
  88. /// <code>
  89. /// public class MyInteraction : IInputInteraction&lt;float&gt;
  90. /// {
  91. /// public void Process(ref IInputInteractionContext context)
  92. /// {
  93. /// if (context.isWaiting && context.ControlIsActuated())
  94. /// {
  95. /// // We've waited for input and got it. Start the interaction.
  96. /// context.Started();
  97. /// }
  98. /// else if (context.isStarted && !context.ControlIsActuated())
  99. /// {
  100. /// // Interaction has been completed.
  101. /// context.Performed();
  102. /// }
  103. /// }
  104. ///
  105. /// public void Reset()
  106. /// {
  107. /// // No reset code needed. We're not keeping any state locally in the interaction.
  108. /// }
  109. /// }
  110. /// </code>
  111. /// </example>
  112. /// </remarks>
  113. public void Started()
  114. {
  115. m_TriggerState.startTime = time;
  116. m_State.ChangePhaseOfInteraction(InputActionPhase.Started, ref m_TriggerState);
  117. }
  118. public void Performed()
  119. {
  120. if (m_TriggerState.phase == InputActionPhase.Waiting)
  121. m_TriggerState.startTime = time;
  122. m_State.ChangePhaseOfInteraction(InputActionPhase.Performed, ref m_TriggerState);
  123. }
  124. public void PerformedAndStayStarted()
  125. {
  126. if (m_TriggerState.phase == InputActionPhase.Waiting)
  127. m_TriggerState.startTime = time;
  128. m_State.ChangePhaseOfInteraction(InputActionPhase.Performed, ref m_TriggerState,
  129. phaseAfterPerformed: InputActionPhase.Started);
  130. }
  131. public void PerformedAndStayPerformed()
  132. {
  133. if (m_TriggerState.phase == InputActionPhase.Waiting)
  134. m_TriggerState.startTime = time;
  135. m_State.ChangePhaseOfInteraction(InputActionPhase.Performed, ref m_TriggerState,
  136. phaseAfterPerformed: InputActionPhase.Performed);
  137. }
  138. public void Canceled()
  139. {
  140. if (m_TriggerState.phase != InputActionPhase.Canceled)
  141. m_State.ChangePhaseOfInteraction(InputActionPhase.Canceled, ref m_TriggerState);
  142. }
  143. public void Waiting()
  144. {
  145. if (m_TriggerState.phase != InputActionPhase.Waiting)
  146. m_State.ChangePhaseOfInteraction(InputActionPhase.Waiting, ref m_TriggerState);
  147. }
  148. public void SetTimeout(float seconds)
  149. {
  150. m_State.StartTimeout(seconds, ref m_TriggerState);
  151. }
  152. public TValue ReadValue<TValue>()
  153. where TValue : struct
  154. {
  155. return m_State.ReadValue<TValue>(m_TriggerState.bindingIndex, m_TriggerState.controlIndex);
  156. }
  157. internal InputActionState m_State;
  158. internal Flags m_Flags;
  159. internal InputActionState.TriggerState m_TriggerState;
  160. internal int mapIndex => m_TriggerState.mapIndex;
  161. internal int controlIndex => m_TriggerState.controlIndex;
  162. internal int bindingIndex => m_TriggerState.bindingIndex;
  163. internal int interactionIndex => m_TriggerState.interactionIndex;
  164. [Flags]
  165. internal enum Flags
  166. {
  167. TimerHasExpired = 1 << 1
  168. }
  169. }
  170. }