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