Brak opisu
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.

DiscreteButtonControl.cs 7.1KB

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