暫無描述
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.

AxisControl.cs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. using System.Runtime.CompilerServices;
  2. using UnityEngine.InputSystem.LowLevel;
  3. using UnityEngine.InputSystem.Processors;
  4. using UnityEngine.InputSystem.Utilities;
  5. ////REVIEW: change 'clampToConstant' to simply 'clampToMin'?
  6. ////TODO: if AxisControl fields where properties, we wouldn't need ApplyParameterChanges, maybe it's ok breaking change?
  7. namespace UnityEngine.InputSystem.Controls
  8. {
  9. /// <summary>
  10. /// A floating-point axis control.
  11. /// </summary>
  12. /// <remarks>
  13. /// Can optionally be configured to perform normalization.
  14. /// Stored as either a float, a short, a byte, or a single bit.
  15. /// </remarks>
  16. public class AxisControl : InputControl<float>
  17. {
  18. /// <summary>
  19. /// Clamping behavior for an axis control.
  20. /// </summary>
  21. public enum Clamp
  22. {
  23. /// <summary>
  24. /// Do not clamp values.
  25. /// </summary>
  26. None = 0,
  27. /// <summary>
  28. /// Clamp values to <see cref="clampMin"/> and <see cref="clampMax"/>
  29. /// before normalizing the value.
  30. /// </summary>
  31. BeforeNormalize = 1,
  32. /// <summary>
  33. /// Clamp values to <see cref="clampMin"/> and <see cref="clampMax"/>
  34. /// after normalizing the value.
  35. /// </summary>
  36. AfterNormalize = 2,
  37. /// <summary>
  38. /// Clamp values any value below <see cref="clampMin"/> or above <see cref="clampMax"/>
  39. /// to <see cref="clampConstant"/> before normalizing the value.
  40. /// </summary>
  41. ToConstantBeforeNormalize = 3,
  42. }
  43. // These can be added as processors but they are so common that we
  44. // build the functionality right into AxisControl to save us an
  45. // additional object and an additional virtual call.
  46. // NOTE: A number of the parameters here can be expressed in much simpler form.
  47. // E.g. 'scale', 'scaleFactor' and 'invert' could all be rolled into a single
  48. // multiplier. And maybe that's what we should do. However, the one advantage
  49. // of the current setup is that it allows to set these operations up individually.
  50. // For example, a given layout may want to have a very specific scale factor but
  51. // then a derived layout needs the value to be inverted. If it was a single setting,
  52. // the derived layout would have to know the specific scale factor in order to come
  53. // up with a valid multiplier.
  54. /// <summary>
  55. /// Clamping behavior when reading values. <see cref="Clamp.None"/> by default.
  56. /// </summary>
  57. /// <value>Clamping behavior.</value>
  58. /// <remarks>
  59. /// When a value is read from the control's state, it is first converted
  60. /// to a floating-point number.
  61. /// </remarks>
  62. /// <seealso cref="clampMin"/>
  63. /// <seealso cref="clampMax"/>
  64. /// <seealso cref="clampConstant"/>
  65. public Clamp clamp;
  66. /// <summary>
  67. /// Lower end of the clamping range when <see cref="clamp"/> is not
  68. /// <see cref="Clamp.None"/>.
  69. /// </summary>
  70. /// <value>Lower bound of clamping range. Inclusive.</value>
  71. public float clampMin;
  72. /// <summary>
  73. /// Upper end of the clamping range when <see cref="clamp"/> is not
  74. /// <see cref="Clamp.None"/>.
  75. /// </summary>
  76. /// <value>Upper bound of clamping range. Inclusive.</value>
  77. public float clampMax;
  78. /// <summary>
  79. /// When <see cref="clamp"/> is set to <see cref="Clamp.ToConstantBeforeNormalize"/>
  80. /// and the value is outside of the range defined by <see cref="clampMin"/> and
  81. /// <see cref="clampMax"/>, this value is returned.
  82. /// </summary>
  83. /// <value>Constant value to return when value is outside of clamping range.</value>
  84. public float clampConstant;
  85. ////REVIEW: why not just roll this into scaleFactor?
  86. /// <summary>
  87. /// If true, the input value will be inverted, i.e. multiplied by -1. Off by default.
  88. /// </summary>
  89. /// <value>Whether to invert the input value.</value>
  90. public bool invert;
  91. /// <summary>
  92. /// If true, normalize the input value to [0..1] or [-1..1] (depending on the
  93. /// value of <see cref="normalizeZero"/>. Off by default.
  94. /// </summary>
  95. /// <value>Whether to normalize input values or not.</value>
  96. /// <seealso cref="normalizeMin"/>
  97. /// <seealso cref="normalizeMax"/>
  98. public bool normalize;
  99. ////REVIEW: shouldn't these come from the control min/max value by default?
  100. /// <summary>
  101. /// If <see cref="normalize"/> is on, this is the input value that corresponds
  102. /// to 0 of the normalized [0..1] or [-1..1] range.
  103. /// </summary>
  104. /// <value>Input value that should become 0 or -1.</value>
  105. /// <remarks>
  106. /// In other words, with <see cref="normalize"/> on, input values are mapped from
  107. /// the range of [normalizeMin..normalizeMax] to [0..1] or [-1..1] (depending on
  108. /// <see cref="normalizeZero"/>).
  109. /// </remarks>
  110. public float normalizeMin;
  111. /// <summary>
  112. /// If <see cref="normalize"/> is on, this is the input value that corresponds
  113. /// to 1 of the normalized [0..1] or [-1..1] range.
  114. /// </summary>
  115. /// <value>Input value that should become 1.</value>
  116. /// <remarks>
  117. /// In other words, with <see cref="normalize"/> on, input values are mapped from
  118. /// the range of [normalizeMin..normalizeMax] to [0..1] or [-1..1] (depending on
  119. /// <see cref="normalizeZero"/>).
  120. /// </remarks>
  121. public float normalizeMax;
  122. /// <summary>
  123. /// Where to put the zero point of the normalization range. Only relevant
  124. /// if <see cref="normalize"/> is set to true. Defaults to 0.
  125. /// </summary>
  126. /// <value>Zero point of normalization range.</value>
  127. /// <remarks>
  128. /// The value of this property determines where the zero point is located in the
  129. /// range established by <see cref="normalizeMin"/> and <see cref="normalizeMax"/>.
  130. ///
  131. /// If <c>normalizeZero</c> is placed at <see cref="normalizeMin"/>, the normalization
  132. /// returns a value in the [0..1] range mapped from the input value range of
  133. /// <see cref="normalizeMin"/> and <see cref="normalizeMax"/>.
  134. ///
  135. /// If <c>normalizeZero</c> is placed in-between <see cref="normalizeMin"/> and
  136. /// <see cref="normalizeMax"/>, normalization returns a value in the [-1..1] mapped
  137. /// from the input value range of <see cref="normalizeMin"/> and <see cref="normalizeMax"/>
  138. /// and the zero point between the two established by <c>normalizeZero</c>.
  139. /// </remarks>
  140. public float normalizeZero;
  141. ////REVIEW: why not just have a default scaleFactor of 1?
  142. /// <summary>
  143. /// Whether the scale the input value by <see cref="scaleFactor"/>. Off by default.
  144. /// </summary>
  145. /// <value>True if inputs should be scaled by <see cref="scaleFactor"/>.</value>
  146. public bool scale;
  147. /// <summary>
  148. /// Value to multiple input values with. Only applied if <see cref="scale"/> is <c>true</c>.
  149. /// </summary>
  150. /// <value>Multiplier for input values.</value>
  151. public float scaleFactor;
  152. /// <summary>
  153. /// Apply modifications to the given value according to the parameters configured
  154. /// on the control (<see cref="clamp"/>, <see cref="normalize"/>, etc).
  155. /// </summary>
  156. /// <param name="value">Input value.</param>
  157. /// <returns>A processed value (clamped, normalized, etc).</returns>
  158. /// <seealso cref="clamp"/>
  159. /// <seealso cref="normalize"/>
  160. /// <seealso cref="scale"/>
  161. /// <seealso cref="invert"/>
  162. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  163. protected float Preprocess(float value)
  164. {
  165. if (scale)
  166. value *= scaleFactor;
  167. if (clamp == Clamp.ToConstantBeforeNormalize)
  168. {
  169. if (value < clampMin || value > clampMax)
  170. value = clampConstant;
  171. }
  172. else if (clamp == Clamp.BeforeNormalize)
  173. value = Mathf.Clamp(value, clampMin, clampMax);
  174. if (normalize)
  175. value = NormalizeProcessor.Normalize(value, normalizeMin, normalizeMax, normalizeZero);
  176. if (clamp == Clamp.AfterNormalize)
  177. value = Mathf.Clamp(value, clampMin, clampMax);
  178. if (invert)
  179. value *= -1.0f;
  180. return value;
  181. }
  182. private float Unpreprocess(float value)
  183. {
  184. // Does not reverse the effect of clamping (we don't know what the unclamped value should be).
  185. if (invert)
  186. value *= -1f;
  187. if (normalize)
  188. value = NormalizeProcessor.Denormalize(value, normalizeMin, normalizeMax, normalizeZero);
  189. if (scale)
  190. value /= scaleFactor;
  191. return value;
  192. }
  193. /// <summary>
  194. /// Default-initialize the control.
  195. /// </summary>
  196. /// <remarks>
  197. /// Defaults the format to <see cref="InputStateBlock.FormatFloat"/>.
  198. /// </remarks>
  199. public AxisControl()
  200. {
  201. m_StateBlock.format = InputStateBlock.FormatFloat;
  202. }
  203. protected override void FinishSetup()
  204. {
  205. base.FinishSetup();
  206. // if we don't have any default state, and we are using normalizeZero, then the default value
  207. // should not be zero. Generate it from normalizeZero.
  208. if (!hasDefaultState && normalize && Mathf.Abs(normalizeZero) > Mathf.Epsilon)
  209. m_DefaultState = stateBlock.FloatToPrimitiveValue(normalizeZero);
  210. }
  211. /// <inheritdoc />
  212. public override unsafe float ReadUnprocessedValueFromState(void* statePtr)
  213. {
  214. switch (m_OptimizedControlDataType)
  215. {
  216. case InputStateBlock.kFormatFloat:
  217. return *(float*)((byte*)statePtr + m_StateBlock.m_ByteOffset);
  218. case InputStateBlock.kFormatByte:
  219. return *((byte*)statePtr + m_StateBlock.m_ByteOffset) != 0 ? 1.0f : 0.0f;
  220. default:
  221. {
  222. var value = stateBlock.ReadFloat(statePtr);
  223. ////REVIEW: this isn't very raw
  224. return Preprocess(value);
  225. }
  226. }
  227. }
  228. /// <inheritdoc />
  229. public override unsafe void WriteValueIntoState(float value, void* statePtr)
  230. {
  231. switch (m_OptimizedControlDataType)
  232. {
  233. case InputStateBlock.kFormatFloat:
  234. *(float*)((byte*)statePtr + m_StateBlock.m_ByteOffset) = value;
  235. break;
  236. case InputStateBlock.kFormatByte:
  237. *((byte*)statePtr + m_StateBlock.m_ByteOffset) = (byte)(value >= 0.5f ? 1 : 0);
  238. break;
  239. default:
  240. value = Unpreprocess(value);
  241. stateBlock.WriteFloat(statePtr, value);
  242. break;
  243. }
  244. }
  245. /// <inheritdoc />
  246. public override unsafe bool CompareValue(void* firstStatePtr, void* secondStatePtr)
  247. {
  248. var currentValue = ReadValueFromState(firstStatePtr);
  249. var valueInState = ReadValueFromState(secondStatePtr);
  250. return !Mathf.Approximately(currentValue, valueInState);
  251. }
  252. /// <inheritdoc />
  253. public override unsafe float EvaluateMagnitude(void* statePtr)
  254. {
  255. return EvaluateMagnitude(ReadValueFromStateWithCaching(statePtr));
  256. }
  257. private float EvaluateMagnitude(float value)
  258. {
  259. if (m_MinValue.isEmpty || m_MaxValue.isEmpty)
  260. return Mathf.Abs(value);
  261. var min = m_MinValue.ToSingle();
  262. var max = m_MaxValue.ToSingle();
  263. var clampedValue = Mathf.Clamp(value, min, max);
  264. // If part of our range is in negative space, evaluate magnitude as two
  265. // separate subspaces.
  266. if (min < 0)
  267. {
  268. if (clampedValue < 0)
  269. return NormalizeProcessor.Normalize(Mathf.Abs(clampedValue), 0, Mathf.Abs(min), 0);
  270. return NormalizeProcessor.Normalize(clampedValue, 0, max, 0);
  271. }
  272. return NormalizeProcessor.Normalize(clampedValue, min, max, 0);
  273. }
  274. protected override FourCC CalculateOptimizedControlDataType()
  275. {
  276. var noProcessingNeeded =
  277. clamp == Clamp.None &&
  278. invert == false &&
  279. normalize == false &&
  280. scale == false;
  281. if (noProcessingNeeded &&
  282. m_StateBlock.format == InputStateBlock.FormatFloat &&
  283. m_StateBlock.sizeInBits == 32 &&
  284. m_StateBlock.bitOffset == 0)
  285. return InputStateBlock.FormatFloat;
  286. if (noProcessingNeeded &&
  287. m_StateBlock.format == InputStateBlock.FormatBit &&
  288. // has to be 8, otherwise we might be mapping to a state which only represents first bit in the byte, while other bits might map to some other controls
  289. // like in the mouse where LMB/RMB map to the same byte, just LMB maps to first bit and RMB maps to second bit
  290. m_StateBlock.sizeInBits == 8 &&
  291. m_StateBlock.bitOffset == 0)
  292. return InputStateBlock.FormatByte;
  293. return InputStateBlock.FormatInvalid;
  294. }
  295. }
  296. }