A game about forced loneliness, made by TACStudios
at master 7.2 kB view raw
1using System; 2using UnityEngine.InputSystem.LowLevel; 3using UnityEngine.InputSystem.Layouts; 4using UnityEngine.InputSystem.Utilities; 5 6////TODO: hide in UI 7 8namespace UnityEngine.InputSystem.Controls 9{ 10 /// <summary> 11 /// A button that is considered pressed if the underlying state has a value in the specific range. 12 /// </summary> 13 /// <remarks> 14 /// This control is most useful for handling HID-style hat switches. Unlike <see cref="DpadControl"/>, 15 /// which by default is stored as a bitfield of four bits that each represent a direction on the pad, 16 /// these hat switches enumerate the possible directions that the switch can be moved in. For example, 17 /// the value 1 could indicate that the switch is moved to the left whereas 3 could indicate it is 18 /// moved up. 19 /// 20 /// <example> 21 /// <code> 22 /// [StructLayout(LayoutKind.Explicit, Size = 32)] 23 /// internal struct DualShock4HIDInputReport : IInputStateTypeInfo 24 /// { 25 /// [FieldOffset(0)] public byte reportId; 26 /// 27 /// [InputControl(name = "dpad", format = "BIT", layout = "Dpad", sizeInBits = 4, defaultState = 8)] 28 /// [InputControl(name = "dpad/up", format = "BIT", layout = "DiscreteButton", parameters = "minValue=7,maxValue=1,nullValue=8,wrapAtValue=7", bit = 0, sizeInBits = 4)] 29 /// [InputControl(name = "dpad/right", format = "BIT", layout = "DiscreteButton", parameters = "minValue=1,maxValue=3", bit = 0, sizeInBits = 4)] 30 /// [InputControl(name = "dpad/down", format = "BIT", layout = "DiscreteButton", parameters = "minValue=3,maxValue=5", bit = 0, sizeInBits = 4)] 31 /// [InputControl(name = "dpad/left", format = "BIT", layout = "DiscreteButton", parameters = "minValue=5, maxValue=7", bit = 0, sizeInBits = 4)] 32 /// [FieldOffset(5)] public byte buttons1; 33 /// } 34 /// </code> 35 /// </example> 36 /// </remarks> 37 public class DiscreteButtonControl : ButtonControl 38 { 39 /// <summary> 40 /// Value (inclusive) at which to start considering the button to be pressed. 41 /// </summary> 42 /// <remarks> 43 /// <see cref="minValue"/> is allowed to be larger than <see cref="maxValue"/>. This indicates 44 /// a setup where the value wraps around beyond <see cref="minValue"/>, skips <see cref="nullValue"/>, 45 /// and then goes all the way up to <see cref="maxValue"/>. 46 /// 47 /// For example, if the underlying state represents a circular D-pad and enumerates its 48 /// 9 possible positions (including null state) going clock-wise from 0 to 8 and with 1 49 /// indicating that the D-pad is pressed to the left, then 1, 2, and 8 would indicate 50 /// that the "left" button is held on the D-pad. To set this up, set <see cref="minValue"/> 51 /// to 8, <see cref="maxValue"/> to 2, and <see cref="nullValue"/> to 0 (the default). 52 /// </remarks> 53 public int minValue; 54 55 /// <summary> 56 /// Value (inclusive) beyond which to stop considering the button to be pressed. 57 /// </summary> 58 public int maxValue; 59 60 /// <summary> 61 /// Value (inclusive) at which the values cut off and wrap back around. Considered to not be set if equal to <see cref="nullValue"/>. 62 /// </summary> 63 /// <remarks> 64 /// This is useful if, for example, an enumeration has more bits than are used for "on" states of controls. For example, 65 /// a bitfield of 4 bits where values 1-7 (i.e. 0001 to 0111) indicate "on" states of controls and value 8 indicating an 66 /// "off" state. In that case, set <see cref="nullValue"/> to 8 and <see cref="wrapAtValue"/> to 7. 67 /// </remarks> 68 public int wrapAtValue; 69 70 /// <summary> 71 /// Value at which the button is considered to not be pressed. Usually zero. Some devices, however, 72 /// use non-zero default states. 73 /// </summary> 74 public int nullValue; 75 76 /// <summary> 77 /// Determines the behavior of <see cref="WriteValueIntoState"/>. By default, attempting to write a <see cref="DiscreteButtonControl"/> 78 /// will result in a <c>NotSupportedException</c>. 79 /// </summary> 80 public WriteMode writeMode; 81 82 protected override void FinishSetup() 83 { 84 base.FinishSetup(); 85 86 if (!stateBlock.format.IsIntegerFormat()) 87 throw new NotSupportedException( 88 $"Non-integer format '{stateBlock.format}' is not supported for DiscreteButtonControl '{this}'"); 89 } 90 91 public override unsafe float ReadUnprocessedValueFromState(void* statePtr) 92 { 93 var valuePtr = (byte*)statePtr + (int)m_StateBlock.byteOffset; 94 // Note that all signed data in state buffers is in excess-K format. 95 var intValue = MemoryHelpers.ReadTwosComplementMultipleBitsAsInt(valuePtr, m_StateBlock.bitOffset, m_StateBlock.sizeInBits); 96 97 var value = 0.0f; 98 if (minValue > maxValue) 99 { 100 // If no wrapping point is set, default to wrapping around exactly 101 // at the point of minValue. 102 if (wrapAtValue == nullValue) 103 wrapAtValue = minValue; 104 105 if ((intValue >= minValue && intValue <= wrapAtValue) 106 || (intValue != nullValue && intValue <= maxValue)) 107 value = 1.0f; 108 } 109 else 110 { 111 value = intValue >= minValue && intValue <= maxValue ? 1.0f : 0.0f; 112 } 113 114 return Preprocess(value); 115 } 116 117 public override unsafe void WriteValueIntoState(float value, void* statePtr) 118 { 119 if (writeMode == WriteMode.WriteNullAndMaxValue) 120 { 121 var valuePtr = (byte*)statePtr + (int)m_StateBlock.byteOffset; 122 var valueToWrite = value >= pressPointOrDefault ? maxValue : nullValue; 123 MemoryHelpers.WriteIntAsTwosComplementMultipleBits(valuePtr, m_StateBlock.bitOffset, m_StateBlock.sizeInBits, valueToWrite); 124 return; 125 } 126 127 // The way these controls are usually used, the state is shared between multiple DiscreteButtons. So writing one 128 // may have unpredictable effects on the value of other buttons. 129 throw new NotSupportedException("Writing value states for DiscreteButtonControl is not supported as a single value may correspond to multiple states"); 130 } 131 132 /// <summary> 133 /// How <see cref="DiscreteButtonControl.WriteValueIntoState"/> should behave. 134 /// </summary> 135 public enum WriteMode 136 { 137 /// <summary> 138 /// <see cref="DiscreteButtonControl.WriteValueIntoState"/> will throw <c>NotSupportedException</c>. 139 /// </summary> 140 WriteDisabled = 0, 141 142 /// <summary> 143 /// Write <see cref="DiscreteButtonControl.nullValue"/> for when the button is considered not pressed and 144 /// write <see cref="DiscreteButtonControl.maxValue"/> for when the button is considered pressed. 145 /// </summary> 146 WriteNullAndMaxValue = 1, 147 } 148 } 149}