No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PressInteraction.cs 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. using System;
  2. using System.ComponentModel;
  3. using UnityEngine.InputSystem.Controls;
  4. using UnityEngine.Scripting;
  5. #if UNITY_EDITOR
  6. using UnityEditor;
  7. using UnityEngine.InputSystem.Editor;
  8. using UnityEngine.UIElements;
  9. using UnityEditor.UIElements;
  10. #endif
  11. ////TODO: protect against the control *hovering* around the press point; this should not fire the press repeatedly; probably need a zone around the press point
  12. ////TODO: also, for analog controls, we probably want a deadzone that gives just a tiny little buffer at the low end before the action starts
  13. ////REVIEW: shouldn't it use Canceled for release on PressAndRelease instead of triggering Performed again?
  14. namespace UnityEngine.InputSystem.Interactions
  15. {
  16. /// <summary>
  17. /// Performs the action at specific points in a button press-and-release sequence according top <see cref="behavior"/>.
  18. /// </summary>
  19. /// <remarks>
  20. /// By default, uses <see cref="PressBehavior.PressOnly"/> which performs the action as soon as the control crosses the
  21. /// button press threshold defined by <see cref="pressPoint"/>. The action then will not trigger again until the control
  22. /// is first released.
  23. ///
  24. /// Can be set to instead trigger on release (that is, when the control goes back below the button press threshold) using
  25. /// <see cref="PressBehavior.ReleaseOnly"/> or can be set to trigger on both press and release using <see cref="PressBehavior.PressAndRelease"/>).
  26. ///
  27. /// Note that using an explicit press interaction is only necessary if the goal is to either customize the press behavior
  28. /// of a button or when binding to controls that are not buttons as such (the press interaction compares magnitudes to
  29. /// <see cref="pressPoint"/> and thus any type of control that can deliver a magnitude can act as a button). The default
  30. /// behavior available out of the box when binding <see cref="InputActionType.Button"/> type actions to button-type controls
  31. /// (<see cref="UnityEngine.InputSystem.Controls.ButtonControl"/>) corresponds to using a press modifier with <see cref="behavior"/>
  32. /// set to <see cref="PressBehavior.PressOnly"/> and <see cref="pressPoint"/> left at default.
  33. /// </remarks>
  34. [DisplayName("Press")]
  35. public class PressInteraction : IInputInteraction
  36. {
  37. /// <summary>
  38. /// Amount of actuation required before a control is considered pressed.
  39. /// </summary>
  40. /// <remarks>
  41. /// If zero (default), defaults to <see cref="InputSettings.defaultButtonPressPoint"/>.
  42. /// </remarks>
  43. [Tooltip("The amount of actuation a control requires before being considered pressed. If not set, default to "
  44. + "'Default Press Point' in the global input settings.")]
  45. public float pressPoint;
  46. ////REVIEW: this should really be named "pressBehavior"
  47. /// <summary>
  48. /// Determines how button presses trigger the action.
  49. /// </summary>
  50. /// <remarks>
  51. /// By default (PressOnly), the action is performed on press.
  52. /// With ReleaseOnly, the action is performed on release. With PressAndRelease, the action is
  53. /// performed on press and on release.
  54. /// </remarks>
  55. [Tooltip("Determines how button presses trigger the action. By default (PressOnly), the action is performed on press. "
  56. + "With ReleaseOnly, the action is performed on release. With PressAndRelease, the action is performed on press and release.")]
  57. public PressBehavior behavior;
  58. private float pressPointOrDefault => pressPoint > 0 ? pressPoint : ButtonControl.s_GlobalDefaultButtonPressPoint;
  59. private float releasePointOrDefault => pressPointOrDefault * ButtonControl.s_GlobalDefaultButtonReleaseThreshold;
  60. private bool m_WaitingForRelease;
  61. public void Process(ref InputInteractionContext context)
  62. {
  63. var actuation = context.ComputeMagnitude();
  64. switch (behavior)
  65. {
  66. case PressBehavior.PressOnly:
  67. if (m_WaitingForRelease)
  68. {
  69. if (actuation <= releasePointOrDefault)
  70. {
  71. m_WaitingForRelease = false;
  72. if (Mathf.Approximately(0f, actuation))
  73. context.Canceled();
  74. else
  75. context.Started();
  76. }
  77. }
  78. else if (actuation >= pressPointOrDefault)
  79. {
  80. m_WaitingForRelease = true;
  81. // Stay performed until release.
  82. context.PerformedAndStayPerformed();
  83. }
  84. else if (actuation > 0 && !context.isStarted)
  85. {
  86. context.Started();
  87. }
  88. else if (Mathf.Approximately(0f, actuation) && context.isStarted)
  89. {
  90. context.Canceled();
  91. }
  92. break;
  93. case PressBehavior.ReleaseOnly:
  94. if (m_WaitingForRelease)
  95. {
  96. if (actuation <= releasePointOrDefault)
  97. {
  98. m_WaitingForRelease = false;
  99. context.Performed();
  100. context.Canceled();
  101. }
  102. }
  103. else if (actuation >= pressPointOrDefault)
  104. {
  105. m_WaitingForRelease = true;
  106. if (!context.isStarted)
  107. context.Started();
  108. }
  109. else
  110. {
  111. var started = context.isStarted;
  112. if (actuation > 0 && !started)
  113. context.Started();
  114. else if (Mathf.Approximately(0, actuation) && started)
  115. context.Canceled();
  116. }
  117. break;
  118. case PressBehavior.PressAndRelease:
  119. if (m_WaitingForRelease)
  120. {
  121. if (actuation <= releasePointOrDefault)
  122. {
  123. m_WaitingForRelease = false;
  124. context.Performed();
  125. if (Mathf.Approximately(0, actuation))
  126. context.Canceled();
  127. }
  128. }
  129. else if (actuation >= pressPointOrDefault)
  130. {
  131. m_WaitingForRelease = true;
  132. context.PerformedAndStayPerformed();
  133. }
  134. else
  135. {
  136. var started = context.isStarted;
  137. if (actuation > 0 && !started)
  138. context.Started();
  139. else if (Mathf.Approximately(0, actuation) && started)
  140. context.Canceled();
  141. }
  142. break;
  143. }
  144. }
  145. public void Reset()
  146. {
  147. m_WaitingForRelease = false;
  148. }
  149. }
  150. /// <summary>
  151. /// Determines how to trigger an action based on button presses.
  152. /// </summary>
  153. /// <seealso cref="PressInteraction.behavior"/>
  154. public enum PressBehavior
  155. {
  156. /// <summary>
  157. /// Perform the action when the button is pressed.
  158. /// </summary>
  159. /// <remarks>
  160. /// Triggers <see cref="InputAction.performed"/> when a control crosses the button press threshold.
  161. /// </remarks>
  162. // ReSharper disable once UnusedMember.Global
  163. PressOnly = 0,
  164. /// <summary>
  165. /// Perform the action when the button is released.
  166. /// </summary>
  167. /// <remarks>
  168. /// Triggers <see cref="InputAction.started"/> when a control crosses the button press threshold and
  169. /// <see cref="InputAction.performed"/> when the control goes back below the button press threshold.
  170. /// </remarks>
  171. // ReSharper disable once UnusedMember.Global
  172. ReleaseOnly = 1,
  173. /// <summary>
  174. /// Perform the action when the button is pressed and when the button is released.
  175. /// </summary>
  176. /// <remarks>
  177. /// Triggers <see cref="InputAction.performed"/> when a control crosses the button press threshold
  178. /// and triggers <see cref="InputAction.performed"/> again when it goes back below the button press
  179. /// threshold.
  180. /// </remarks>
  181. // ReSharper disable once UnusedMember.Global
  182. PressAndRelease = 2,
  183. }
  184. #if UNITY_EDITOR
  185. /// <summary>
  186. /// UI that is displayed when editing <see cref="PressInteraction"/> in the editor.
  187. /// </summary>
  188. // ReSharper disable once UnusedMember.Global
  189. internal class PressInteractionEditor : InputParameterEditor<PressInteraction>
  190. {
  191. protected override void OnEnable()
  192. {
  193. m_PressPointSetting.Initialize("Press Point",
  194. "The amount of actuation a control requires before being considered pressed. If not set, default to "
  195. + "'Default Button Press Point' in the global input settings.",
  196. "Default Button Press Point",
  197. () => target.pressPoint, v => target.pressPoint = v,
  198. () => InputSystem.settings.defaultButtonPressPoint);
  199. }
  200. public override void OnGUI()
  201. {
  202. EditorGUILayout.HelpBox(s_HelpBoxText);
  203. target.behavior = (PressBehavior)EditorGUILayout.EnumPopup(s_PressBehaviorLabel, target.behavior);
  204. m_PressPointSetting.OnGUI();
  205. }
  206. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  207. public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
  208. {
  209. root.Add(new HelpBox(s_HelpBoxText.text, HelpBoxMessageType.None));
  210. var behaviourDropdown = new EnumField(s_PressBehaviorLabel.text, target.behavior)
  211. {
  212. tooltip = s_PressBehaviorLabel.tooltip
  213. };
  214. behaviourDropdown.RegisterValueChangedCallback(evt =>
  215. {
  216. target.behavior = (PressBehavior)evt.newValue;
  217. onChangedCallback?.Invoke();
  218. });
  219. root.Add(behaviourDropdown);
  220. m_PressPointSetting.OnDrawVisualElements(root, onChangedCallback);
  221. }
  222. #endif
  223. private CustomOrDefaultSetting m_PressPointSetting;
  224. private static readonly GUIContent s_HelpBoxText = EditorGUIUtility.TrTextContent("Note that the 'Press' interaction is only "
  225. + "necessary when wanting to customize button press behavior. For default press behavior, simply set the action type to 'Button' "
  226. + "and use the action without interactions added to it.");
  227. private static readonly GUIContent s_PressBehaviorLabel = EditorGUIUtility.TrTextContent("Trigger Behavior",
  228. "Determines how button presses trigger the action. By default (PressOnly), the action is performed on press. "
  229. + "With ReleaseOnly, the action is performed on release. With PressAndRelease, the action is performed on press and "
  230. + "canceled on release.");
  231. }
  232. #endif
  233. }