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.

OneModifierComposite.cs 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. using System;
  2. using System.ComponentModel;
  3. using Unity.Collections.LowLevel.Unsafe;
  4. using UnityEngine.InputSystem.Layouts;
  5. using UnityEngine.InputSystem.Utilities;
  6. using UnityEngine.Scripting;
  7. ////TODO: allow making modifier optional; maybe alter the value (e.g. 0=unpressed, 0.5=pressed without modifier, 1=pressed with modifier)
  8. namespace UnityEngine.InputSystem.Composites
  9. {
  10. /// <summary>
  11. /// A binding with an additional modifier. The bound controls only trigger when
  12. /// the modifier is pressed.
  13. /// </summary>
  14. /// <remarks>
  15. /// This composite can be used to require a button to be held in order to "activate"
  16. /// another binding. This is most commonly used on keyboards to require one of the
  17. /// modifier keys (shift, ctrl, or alt) to be held in combination with another control,
  18. /// e.g. "CTRL+1".
  19. ///
  20. /// <example>
  21. /// <code>
  22. /// // Create a button action that triggers when CTRL+1
  23. /// // is pressed on the keyboard.
  24. /// var action = new InputAction(type: InputActionType.Button);
  25. /// action.AddCompositeBinding("OneModifier")
  26. /// .With("Modifier", "&lt;Keyboard&gt;/ctrl")
  27. /// .With("Binding", "&lt;Keyboard&gt;/1")
  28. /// </code>
  29. /// </example>
  30. ///
  31. /// However, this can also be used to "gate" other types of controls. For example, a "look"
  32. /// action could be bound to mouse <see cref="Pointer.delta"/> such that the <see cref="Keyboard.altKey"/> on the
  33. /// keyboard has to be pressed in order for the player to be able to look around.
  34. ///
  35. /// <example>
  36. /// <code>
  37. /// lookAction.AddCompositeBinding("OneModifier")
  38. /// .With("Modifier", "&lt;Keyboard&gt;/alt")
  39. /// .With("Binding", "&lt;Mouse&gt;/delta")
  40. /// </code>
  41. /// </example>
  42. /// </remarks>
  43. /// <seealso cref="TwoModifiersComposite"/>
  44. [DisplayStringFormat("{modifier}+{binding}")]
  45. [DisplayName("Binding With One Modifier")]
  46. public class OneModifierComposite : InputBindingComposite
  47. {
  48. /// <summary>
  49. /// Binding for the button that acts as a modifier, e.g. <c>&lt;Keyboard/ctrl</c>.
  50. /// </summary>
  51. /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
  52. /// <remarks>
  53. /// This property is automatically assigned by the input system.
  54. /// </remarks>
  55. // ReSharper disable once MemberCanBePrivate.Global
  56. // ReSharper disable once FieldCanBeMadeReadOnly.Global
  57. // ReSharper disable once UnassignedField.Global
  58. [InputControl(layout = "Button")] public int modifier;
  59. /// <summary>
  60. /// Binding for the control that is gated by the modifier. The composite will assume the value
  61. /// of this control while the modifier is considered pressed (that is, has a magnitude equal to or
  62. /// greater than the button press point).
  63. /// </summary>
  64. /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
  65. /// <remarks>
  66. /// This property is automatically assigned by the input system.
  67. /// </remarks>
  68. // ReSharper disable once MemberCanBePrivate.Global
  69. // ReSharper disable once FieldCanBeMadeReadOnly.Global
  70. // ReSharper disable once UnassignedField.Global
  71. [InputControl] public int binding;
  72. /// <summary>
  73. /// Type of values read from controls bound to <see cref="binding"/>.
  74. /// </summary>
  75. public override Type valueType => m_ValueType;
  76. /// <summary>
  77. /// Size of the largest value that may be read from the controls bound to <see cref="binding"/>.
  78. /// </summary>
  79. public override int valueSizeInBytes => m_ValueSizeInBytes;
  80. /// <summary>
  81. /// If set to <c>true</c>, the built-in logic to determine if modifiers need to be pressed first is overridden.
  82. /// Default value is <c>false</c>.
  83. /// </summary>
  84. /// <remarks>
  85. /// By default, if <see cref="binding"/> is bound to only <see cref="Controls.ButtonControl"/>s, then the composite requires
  86. /// <see cref="modifier"/> to be pressed <em>before</em> pressing <see cref="binding"/>. This means that binding to, for example,
  87. /// <c>Ctrl+B</c>, the <c>ctrl</c> keys have to be pressed before pressing the <c>B</c> key. This is the behavior usually expected
  88. /// with keyboard shortcuts.
  89. ///
  90. /// However, when binding, for example, <c>Ctrl+MouseDelta</c>, it should be possible to press <c>ctrl</c> at any time. The default
  91. /// logic will automatically detect the difference between this binding and the button binding in the example above and behave
  92. /// accordingly.
  93. ///
  94. /// This field allows you to explicitly override this default inference and make it so that regardless of what <see cref="binding"/>
  95. /// is bound to, any press sequence is acceptable. For the example binding to <c>Ctrl+B</c>, it would mean that pressing <c>B</c> and
  96. /// only then pressing <c>Ctrl</c> will still trigger the binding.
  97. /// </remarks>
  98. public bool overrideModifiersNeedToBePressedFirst;
  99. private int m_ValueSizeInBytes;
  100. private Type m_ValueType;
  101. private bool m_BindingIsButton;
  102. public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
  103. {
  104. if (ModifierIsPressed(ref context))
  105. return context.EvaluateMagnitude(binding);
  106. return default;
  107. }
  108. /// <inheritdoc/>
  109. public override unsafe void ReadValue(ref InputBindingCompositeContext context, void* buffer, int bufferSize)
  110. {
  111. if (ModifierIsPressed(ref context))
  112. context.ReadValue(binding, buffer, bufferSize);
  113. else
  114. UnsafeUtility.MemClear(buffer, m_ValueSizeInBytes);
  115. }
  116. private bool ModifierIsPressed(ref InputBindingCompositeContext context)
  117. {
  118. var modifierDown = context.ReadValueAsButton(modifier);
  119. // When the modifiers are gating a button, we require the modifiers to be pressed *first*.
  120. if (modifierDown && m_BindingIsButton && !overrideModifiersNeedToBePressedFirst)
  121. {
  122. var timestamp = context.GetPressTime(binding);
  123. var timestamp1 = context.GetPressTime(modifier);
  124. return timestamp1 <= timestamp;
  125. }
  126. return modifierDown;
  127. }
  128. /// <inheritdoc/>
  129. protected override void FinishSetup(ref InputBindingCompositeContext context)
  130. {
  131. DetermineValueTypeAndSize(ref context, binding, out m_ValueType, out m_ValueSizeInBytes, out m_BindingIsButton);
  132. if (!overrideModifiersNeedToBePressedFirst)
  133. overrideModifiersNeedToBePressedFirst = !InputSystem.settings.shortcutKeysConsumeInput;
  134. }
  135. public override object ReadValueAsObject(ref InputBindingCompositeContext context)
  136. {
  137. if (context.ReadValueAsButton(modifier))
  138. return context.ReadValueAsObject(binding);
  139. return null;
  140. }
  141. internal static void DetermineValueTypeAndSize(ref InputBindingCompositeContext context, int part, out Type valueType, out int valueSizeInBytes, out bool isButton)
  142. {
  143. valueSizeInBytes = 0;
  144. isButton = true;
  145. Type type = null;
  146. foreach (var control in context.controls)
  147. {
  148. if (control.part != part)
  149. continue;
  150. var controlType = control.control.valueType;
  151. if (type == null || controlType.IsAssignableFrom(type))
  152. type = controlType;
  153. else if (!type.IsAssignableFrom(controlType))
  154. type = typeof(Object);
  155. valueSizeInBytes = Math.Max(control.control.valueSizeInBytes, valueSizeInBytes);
  156. // *All* bound controls need to be buttons for us to classify this part as a "Button" part.
  157. isButton &= control.control.isButton;
  158. }
  159. valueType = type;
  160. }
  161. }
  162. }