UI 자동화를 위해 바인딩 기능 구현
- 유니티 에셋 인증 오류로 meta 재생성
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
////REVIEW: generalize this to AnyButton and add to more devices?
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A control that simply checks the entire state it's been assigned
|
||||
/// for whether there's any non-zero bytes. If there are, the control
|
||||
/// returns 1.0; otherwise it returns 0.0.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This control is used by <see cref="Keyboard.anyKey"/> to create a button
|
||||
/// that is toggled on as long as any of the keys on the keyboard is pressed.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Keyboard.anyKey"/>
|
||||
[InputControlLayout(hideInUI = true)]
|
||||
public class AnyKeyControl : ButtonControl
|
||||
{
|
||||
////TODO: wasPressedThisFrame and wasReleasedThisFrame
|
||||
|
||||
/// <summary>
|
||||
/// Default initialization. Sets state size to 1 bit and format to
|
||||
/// <see cref="InputStateBlock.FormatBit"/>.
|
||||
/// </summary>
|
||||
public AnyKeyControl()
|
||||
{
|
||||
m_StateBlock.sizeInBits = 1; // Should be overridden by whoever uses the control.
|
||||
m_StateBlock.format = InputStateBlock.FormatBit;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe float ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
return this.CheckStateIsAtDefault(statePtr) ? 0.0f : 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e444fe6609243258e0a7746c163373a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,334 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Processors;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////REVIEW: change 'clampToConstant' to simply 'clampToMin'?
|
||||
|
||||
////TODO: if AxisControl fields where properties, we wouldn't need ApplyParameterChanges, maybe it's ok breaking change?
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A floating-point axis control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can optionally be configured to perform normalization.
|
||||
/// Stored as either a float, a short, a byte, or a single bit.
|
||||
/// </remarks>
|
||||
public class AxisControl : InputControl<float>
|
||||
{
|
||||
/// <summary>
|
||||
/// Clamping behavior for an axis control.
|
||||
/// </summary>
|
||||
public enum Clamp
|
||||
{
|
||||
/// <summary>
|
||||
/// Do not clamp values.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Clamp values to <see cref="clampMin"/> and <see cref="clampMax"/>
|
||||
/// before normalizing the value.
|
||||
/// </summary>
|
||||
BeforeNormalize = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Clamp values to <see cref="clampMin"/> and <see cref="clampMax"/>
|
||||
/// after normalizing the value.
|
||||
/// </summary>
|
||||
AfterNormalize = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Clamp values any value below <see cref="clampMin"/> or above <see cref="clampMax"/>
|
||||
/// to <see cref="clampConstant"/> before normalizing the value.
|
||||
/// </summary>
|
||||
ToConstantBeforeNormalize = 3,
|
||||
}
|
||||
|
||||
// These can be added as processors but they are so common that we
|
||||
// build the functionality right into AxisControl to save us an
|
||||
// additional object and an additional virtual call.
|
||||
|
||||
// NOTE: A number of the parameters here can be expressed in much simpler form.
|
||||
// E.g. 'scale', 'scaleFactor' and 'invert' could all be rolled into a single
|
||||
// multiplier. And maybe that's what we should do. However, the one advantage
|
||||
// of the current setup is that it allows to set these operations up individually.
|
||||
// For example, a given layout may want to have a very specific scale factor but
|
||||
// then a derived layout needs the value to be inverted. If it was a single setting,
|
||||
// the derived layout would have to know the specific scale factor in order to come
|
||||
// up with a valid multiplier.
|
||||
|
||||
/// <summary>
|
||||
/// Clamping behavior when reading values. <see cref="Clamp.None"/> by default.
|
||||
/// </summary>
|
||||
/// <value>Clamping behavior.</value>
|
||||
/// <remarks>
|
||||
/// When a value is read from the control's state, it is first converted
|
||||
/// to a floating-point number.
|
||||
/// </remarks>
|
||||
/// <seealso cref="clampMin"/>
|
||||
/// <seealso cref="clampMax"/>
|
||||
/// <seealso cref="clampConstant"/>
|
||||
public Clamp clamp;
|
||||
|
||||
/// <summary>
|
||||
/// Lower end of the clamping range when <see cref="clamp"/> is not
|
||||
/// <see cref="Clamp.None"/>.
|
||||
/// </summary>
|
||||
/// <value>Lower bound of clamping range. Inclusive.</value>
|
||||
public float clampMin;
|
||||
|
||||
/// <summary>
|
||||
/// Upper end of the clamping range when <see cref="clamp"/> is not
|
||||
/// <see cref="Clamp.None"/>.
|
||||
/// </summary>
|
||||
/// <value>Upper bound of clamping range. Inclusive.</value>
|
||||
public float clampMax;
|
||||
|
||||
/// <summary>
|
||||
/// When <see cref="clamp"/> is set to <see cref="Clamp.ToConstantBeforeNormalize"/>
|
||||
/// and the value is outside of the range defined by <see cref="clampMin"/> and
|
||||
/// <see cref="clampMax"/>, this value is returned.
|
||||
/// </summary>
|
||||
/// <value>Constant value to return when value is outside of clamping range.</value>
|
||||
public float clampConstant;
|
||||
|
||||
////REVIEW: why not just roll this into scaleFactor?
|
||||
/// <summary>
|
||||
/// If true, the input value will be inverted, i.e. multiplied by -1. Off by default.
|
||||
/// </summary>
|
||||
/// <value>Whether to invert the input value.</value>
|
||||
public bool invert;
|
||||
|
||||
/// <summary>
|
||||
/// If true, normalize the input value to [0..1] or [-1..1] (depending on the
|
||||
/// value of <see cref="normalizeZero"/>. Off by default.
|
||||
/// </summary>
|
||||
/// <value>Whether to normalize input values or not.</value>
|
||||
/// <seealso cref="normalizeMin"/>
|
||||
/// <seealso cref="normalizeMax"/>
|
||||
public bool normalize;
|
||||
|
||||
////REVIEW: shouldn't these come from the control min/max value by default?
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="normalize"/> is on, this is the input value that corresponds
|
||||
/// to 0 of the normalized [0..1] or [-1..1] range.
|
||||
/// </summary>
|
||||
/// <value>Input value that should become 0 or -1.</value>
|
||||
/// <remarks>
|
||||
/// In other words, with <see cref="normalize"/> on, input values are mapped from
|
||||
/// the range of [normalizeMin..normalizeMax] to [0..1] or [-1..1] (depending on
|
||||
/// <see cref="normalizeZero"/>).
|
||||
/// </remarks>
|
||||
public float normalizeMin;
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="normalize"/> is on, this is the input value that corresponds
|
||||
/// to 1 of the normalized [0..1] or [-1..1] range.
|
||||
/// </summary>
|
||||
/// <value>Input value that should become 1.</value>
|
||||
/// <remarks>
|
||||
/// In other words, with <see cref="normalize"/> on, input values are mapped from
|
||||
/// the range of [normalizeMin..normalizeMax] to [0..1] or [-1..1] (depending on
|
||||
/// <see cref="normalizeZero"/>).
|
||||
/// </remarks>
|
||||
public float normalizeMax;
|
||||
|
||||
/// <summary>
|
||||
/// Where to put the zero point of the normalization range. Only relevant
|
||||
/// if <see cref="normalize"/> is set to true. Defaults to 0.
|
||||
/// </summary>
|
||||
/// <value>Zero point of normalization range.</value>
|
||||
/// <remarks>
|
||||
/// The value of this property determines where the zero point is located in the
|
||||
/// range established by <see cref="normalizeMin"/> and <see cref="normalizeMax"/>.
|
||||
///
|
||||
/// If <c>normalizeZero</c> is placed at <see cref="normalizeMin"/>, the normalization
|
||||
/// returns a value in the [0..1] range mapped from the input value range of
|
||||
/// <see cref="normalizeMin"/> and <see cref="normalizeMax"/>.
|
||||
///
|
||||
/// If <c>normalizeZero</c> is placed in-between <see cref="normalizeMin"/> and
|
||||
/// <see cref="normalizeMax"/>, normalization returns a value in the [-1..1] mapped
|
||||
/// from the input value range of <see cref="normalizeMin"/> and <see cref="normalizeMax"/>
|
||||
/// and the zero point between the two established by <c>normalizeZero</c>.
|
||||
/// </remarks>
|
||||
public float normalizeZero;
|
||||
|
||||
////REVIEW: why not just have a default scaleFactor of 1?
|
||||
|
||||
/// <summary>
|
||||
/// Whether the scale the input value by <see cref="scaleFactor"/>. Off by default.
|
||||
/// </summary>
|
||||
/// <value>True if inputs should be scaled by <see cref="scaleFactor"/>.</value>
|
||||
public bool scale;
|
||||
|
||||
/// <summary>
|
||||
/// Value to multiple input values with. Only applied if <see cref="scale"/> is <c>true</c>.
|
||||
/// </summary>
|
||||
/// <value>Multiplier for input values.</value>
|
||||
public float scaleFactor;
|
||||
|
||||
/// <summary>
|
||||
/// Apply modifications to the given value according to the parameters configured
|
||||
/// on the control (<see cref="clamp"/>, <see cref="normalize"/>, etc).
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <returns>A processed value (clamped, normalized, etc).</returns>
|
||||
/// <seealso cref="clamp"/>
|
||||
/// <seealso cref="normalize"/>
|
||||
/// <seealso cref="scale"/>
|
||||
/// <seealso cref="invert"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected float Preprocess(float value)
|
||||
{
|
||||
if (scale)
|
||||
value *= scaleFactor;
|
||||
if (clamp == Clamp.ToConstantBeforeNormalize)
|
||||
{
|
||||
if (value < clampMin || value > clampMax)
|
||||
value = clampConstant;
|
||||
}
|
||||
else if (clamp == Clamp.BeforeNormalize)
|
||||
value = Mathf.Clamp(value, clampMin, clampMax);
|
||||
if (normalize)
|
||||
value = NormalizeProcessor.Normalize(value, normalizeMin, normalizeMax, normalizeZero);
|
||||
if (clamp == Clamp.AfterNormalize)
|
||||
value = Mathf.Clamp(value, clampMin, clampMax);
|
||||
if (invert)
|
||||
value *= -1.0f;
|
||||
return value;
|
||||
}
|
||||
|
||||
private float Unpreprocess(float value)
|
||||
{
|
||||
// Does not reverse the effect of clamping (we don't know what the unclamped value should be).
|
||||
|
||||
if (invert)
|
||||
value *= -1f;
|
||||
if (normalize)
|
||||
value = NormalizeProcessor.Denormalize(value, normalizeMin, normalizeMax, normalizeZero);
|
||||
if (scale)
|
||||
value /= scaleFactor;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default-initialize the control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Defaults the format to <see cref="InputStateBlock.FormatFloat"/>.
|
||||
/// </remarks>
|
||||
public AxisControl()
|
||||
{
|
||||
m_StateBlock.format = InputStateBlock.FormatFloat;
|
||||
}
|
||||
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
|
||||
// if we don't have any default state, and we are using normalizeZero, then the default value
|
||||
// should not be zero. Generate it from normalizeZero.
|
||||
if (!hasDefaultState && normalize && Mathf.Abs(normalizeZero) > Mathf.Epsilon)
|
||||
m_DefaultState = stateBlock.FloatToPrimitiveValue(normalizeZero);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe float ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
switch (m_OptimizedControlDataType)
|
||||
{
|
||||
case InputStateBlock.kFormatFloat:
|
||||
return *(float*)((byte*)statePtr + m_StateBlock.m_ByteOffset);
|
||||
case InputStateBlock.kFormatByte:
|
||||
return *((byte*)statePtr + m_StateBlock.m_ByteOffset) != 0 ? 1.0f : 0.0f;
|
||||
default:
|
||||
{
|
||||
var value = stateBlock.ReadFloat(statePtr);
|
||||
////REVIEW: this isn't very raw
|
||||
return Preprocess(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe void WriteValueIntoState(float value, void* statePtr)
|
||||
{
|
||||
switch (m_OptimizedControlDataType)
|
||||
{
|
||||
case InputStateBlock.kFormatFloat:
|
||||
*(float*)((byte*)statePtr + m_StateBlock.m_ByteOffset) = value;
|
||||
break;
|
||||
case InputStateBlock.kFormatByte:
|
||||
*((byte*)statePtr + m_StateBlock.m_ByteOffset) = (byte)(value >= 0.5f ? 1 : 0);
|
||||
break;
|
||||
default:
|
||||
value = Unpreprocess(value);
|
||||
stateBlock.WriteFloat(statePtr, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe bool CompareValue(void* firstStatePtr, void* secondStatePtr)
|
||||
{
|
||||
var currentValue = ReadValueFromState(firstStatePtr);
|
||||
var valueInState = ReadValueFromState(secondStatePtr);
|
||||
return !Mathf.Approximately(currentValue, valueInState);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe float EvaluateMagnitude(void* statePtr)
|
||||
{
|
||||
return EvaluateMagnitude(ReadValueFromStateWithCaching(statePtr));
|
||||
}
|
||||
|
||||
private float EvaluateMagnitude(float value)
|
||||
{
|
||||
if (m_MinValue.isEmpty || m_MaxValue.isEmpty)
|
||||
return Mathf.Abs(value);
|
||||
|
||||
var min = m_MinValue.ToSingle();
|
||||
var max = m_MaxValue.ToSingle();
|
||||
|
||||
var clampedValue = Mathf.Clamp(value, min, max);
|
||||
|
||||
// If part of our range is in negative space, evaluate magnitude as two
|
||||
// separate subspaces.
|
||||
if (min < 0)
|
||||
{
|
||||
if (clampedValue < 0)
|
||||
return NormalizeProcessor.Normalize(Mathf.Abs(clampedValue), 0, Mathf.Abs(min), 0);
|
||||
return NormalizeProcessor.Normalize(clampedValue, 0, max, 0);
|
||||
}
|
||||
|
||||
return NormalizeProcessor.Normalize(clampedValue, min, max, 0);
|
||||
}
|
||||
|
||||
protected override FourCC CalculateOptimizedControlDataType()
|
||||
{
|
||||
var noProcessingNeeded =
|
||||
clamp == Clamp.None &&
|
||||
invert == false &&
|
||||
normalize == false &&
|
||||
scale == false;
|
||||
|
||||
if (noProcessingNeeded &&
|
||||
m_StateBlock.format == InputStateBlock.FormatFloat &&
|
||||
m_StateBlock.sizeInBits == 32 &&
|
||||
m_StateBlock.bitOffset == 0)
|
||||
return InputStateBlock.FormatFloat;
|
||||
if (noProcessingNeeded &&
|
||||
m_StateBlock.format == InputStateBlock.FormatBit &&
|
||||
// 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
|
||||
// like in the mouse where LMB/RMB map to the same byte, just LMB maps to first bit and RMB maps to second bit
|
||||
m_StateBlock.sizeInBits == 8 &&
|
||||
m_StateBlock.bitOffset == 0)
|
||||
return InputStateBlock.FormatByte;
|
||||
return InputStateBlock.FormatInvalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 551b733126e34f71a7294051cf2bfc0b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,387 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
////REVIEW: introduce separate base class for ButtonControl and AxisControl instead of deriving ButtonControl from AxisControl?
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// An axis that has a trigger point beyond which it is considered to be pressed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By default, stored as a single bit. In that format, buttons will only yield 0
|
||||
/// and 1 as values. However, buttons return are <see cref="AxisControl"/>s and
|
||||
/// yield full floating-point values and may thus have a range of values. See
|
||||
/// <see cref="pressPoint"/> for how button presses on such buttons are handled.
|
||||
/// </remarks>
|
||||
public class ButtonControl : AxisControl
|
||||
{
|
||||
private bool m_NeedsToCheckFramePress = false;
|
||||
private uint m_UpdateCountLastPressed = uint.MaxValue;
|
||||
private uint m_UpdateCountLastReleased = uint.MaxValue;
|
||||
private bool m_LastUpdateWasPress;
|
||||
#if UNITY_EDITOR
|
||||
// Editor input updates have a separate block of state memory, so must be checked separately
|
||||
private uint m_UpdateCountLastPressedEditor = uint.MaxValue;
|
||||
private uint m_UpdateCountLastReleasedEditor = uint.MaxValue;
|
||||
private bool m_LastUpdateWasPressEditor;
|
||||
#endif
|
||||
|
||||
internal bool needsToCheckFramePress { get; private set; }
|
||||
|
||||
////REVIEW: are per-control press points really necessary? can we just drop them?
|
||||
/// <summary>
|
||||
/// The minimum value the button has to reach for it to be considered pressed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The button is considered pressed, if it has a value equal to or greater than
|
||||
/// this value.
|
||||
///
|
||||
/// By default, this property is set to -1. If the value of the property is negative,
|
||||
/// <see cref="InputSettings.defaultButtonPressPoint"/> is used.
|
||||
///
|
||||
/// The value can be configured as a parameter in a layout.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.InputSystem.Controls;
|
||||
///
|
||||
/// public class MyDevice : InputDevice
|
||||
/// {
|
||||
/// [InputControl(parameters = "pressPoint=0.234")]
|
||||
/// public ButtonControl button { get; private set; }
|
||||
///
|
||||
/// //...
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public float pressPoint = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Return <see cref="pressPoint"/> if set, otherwise return <see cref="InputSettings.defaultButtonPressPoint"/>.
|
||||
/// </summary>
|
||||
public float pressPointOrDefault => pressPoint > 0 ? pressPoint : s_GlobalDefaultButtonPressPoint;
|
||||
|
||||
/// <summary>
|
||||
/// Default-initialize the button control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default format for the button control is <see cref="InputStateBlock.FormatBit"/>.
|
||||
/// The button control's minimum value is set to 0 and the maximum value to 1.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.InputSystem.Controls;
|
||||
///
|
||||
/// public class ButtonControlExample : MonoBehaviour
|
||||
/// {
|
||||
/// void Start()
|
||||
/// {
|
||||
/// var myButton = new ButtonControl();
|
||||
/// }
|
||||
///
|
||||
/// //...
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="AxisControl"/>
|
||||
public ButtonControl()
|
||||
{
|
||||
m_StateBlock.format = InputStateBlock.FormatBit;
|
||||
m_MinValue = 0f;
|
||||
m_MaxValue = 1f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given value would be considered pressed for this button.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to check for if the button would be considered pressed or not.</param>
|
||||
/// <returns>True if <paramref name="value"/> crosses the threshold to be considered pressed.</returns>
|
||||
/// <remarks>
|
||||
/// The default format for the control is <see cref="InputStateBlock.FormatBit"/>.
|
||||
/// The control's minimum value is set to 0 and the maximum value to 1.
|
||||
/// See <see cref="InputSettings.defaultButtonPressPoint"/> and <see cref="pressPoint"/>for the (default) press point.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.InputSystem.Controls;
|
||||
///
|
||||
/// public class IsValueConsideredPressedExample : MonoBehaviour
|
||||
/// {
|
||||
/// void Start()
|
||||
/// {
|
||||
/// var myButton = new ButtonControl();
|
||||
/// var valueToTest = 0.5f;
|
||||
///
|
||||
/// if (myButton.IsValueConsideredPressed(valueToTest))
|
||||
/// {
|
||||
/// Debug.Log("myButton is considered pressed at: " + valueToTest.ToString());
|
||||
/// }
|
||||
/// else
|
||||
/// {
|
||||
/// Debug.Log("myButton is not considered pressed at: " + valueToTest.ToString());
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// //...
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public new bool IsValueConsideredPressed(float value)
|
||||
{
|
||||
return value >= pressPointOrDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the button is currently pressed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A button is considered pressed if its value is equal to or greater
|
||||
/// than its button press threshold (<see cref="pressPointOrDefault"/>, <see cref="pressPoint"/>).
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para>You can use this to read whether specific keys are currently pressed by using isPressed on keys, as shown in the following examples:</para>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// // Using KeyControl property directly.
|
||||
/// Keyboard.current.spaceKey.isPressed
|
||||
/// Keyboard.current.aKey.isPressed // etc.
|
||||
///
|
||||
/// // Using Key enum.
|
||||
/// Keyboard.current[Key.Space].isPressed
|
||||
///
|
||||
/// // Using key name.
|
||||
/// ((KeyControl)Keyboard.current["space"]).isPressed
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// <para>Note: The Input System identifies keys by physical layout, not according to the current language mapping of the keyboard. To query the name of the key according to the language mapping, use <see cref="InputControl.displayName"/>.
|
||||
///
|
||||
/// You can also use this to read mouse buttons, as shown in the following examples:</para>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// bool leftPressed = Mouse.current.leftButton.isPressed;
|
||||
/// bool rightPressed = Mouse.current.rightButton.isPressed;
|
||||
/// bool middlePressed = Mouse.current.middleButton.isPressed;
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// <para>You can also check through all numbered buttons on the mouse: (this example does not cause allocations)</para>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// var controls = Mouse.current.allControls;
|
||||
/// for (var i = 0; i < controls.Count; ++i)
|
||||
/// {
|
||||
/// var button = controls[i] as ButtonControl;
|
||||
/// if (button != null && button.isPressed)
|
||||
/// {
|
||||
/// // respond to mouse button press here...
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// <para>Or you can look up controls by name, like this:</para>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// bool leftPressed = ((ButtonControl)Mouse.current["leftButton"]).isPressed;
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool isPressed
|
||||
{
|
||||
get
|
||||
{
|
||||
// Take the old path if we don't have the speed gain from already testing wasPressedThisFrame/wasReleasedThisFrame.
|
||||
if (!needsToCheckFramePress)
|
||||
return IsValueConsideredPressed(value);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (InputUpdate.s_LatestUpdateType.IsEditorUpdate())
|
||||
return m_LastUpdateWasPressEditor;
|
||||
#endif
|
||||
|
||||
return m_LastUpdateWasPress;
|
||||
}
|
||||
}
|
||||
|
||||
// When we start caring about inter-frame presses, use the info we have to set up the alternate path.
|
||||
// If we don't do this, users could call wasPressedThisFrame/wasReleasedThisFrame twice for the first time in
|
||||
// a single frame, and the returned value may be incorrect until the next frame.
|
||||
private void BeginTestingForFramePresses(bool currentlyPressed, bool pressedLastFrame)
|
||||
{
|
||||
needsToCheckFramePress = true;
|
||||
device.m_ButtonControlsCheckingPressState.Add(this);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (InputUpdate.s_LatestUpdateType.IsEditorUpdate())
|
||||
{
|
||||
m_LastUpdateWasPressEditor = currentlyPressed;
|
||||
if (currentlyPressed && !pressedLastFrame)
|
||||
m_UpdateCountLastPressedEditor = device.m_CurrentUpdateStepCount;
|
||||
else if (pressedLastFrame && !currentlyPressed)
|
||||
m_UpdateCountLastReleasedEditor = device.m_CurrentUpdateStepCount;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
m_LastUpdateWasPress = currentlyPressed;
|
||||
if (currentlyPressed && !pressedLastFrame)
|
||||
m_UpdateCountLastPressed = device.m_CurrentUpdateStepCount;
|
||||
else if (pressedLastFrame && !currentlyPressed)
|
||||
m_UpdateCountLastReleased = device.m_CurrentUpdateStepCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the press started this frame.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The first time this function - or wasReleasedThisFrame - are called, it's possible that extremely fast
|
||||
/// inputs (or very slow frame update times) will result in presses/releases being missed.
|
||||
/// Following the next input system update after either have been called, and from then on until the device is
|
||||
/// destroyed, this ceases to be an issue.
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // An example showing the use of this property on a gamepad button and a keyboard key.
|
||||
///
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.InputSystem;
|
||||
///
|
||||
/// public class ExampleScript : MonoBehaviour
|
||||
/// {
|
||||
/// void Update()
|
||||
/// {
|
||||
/// bool buttonPressed = Gamepad.current.aButton.wasPressedThisFrame;
|
||||
/// bool spaceKeyPressed = Keyboard.current.spaceKey.wasPressedThisFrame;
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// _Note_: The Input System identifies keys by physical layout, not according to the current language mapping of the keyboard. To query the name of the key according to the language mapping, use <see cref="InputControl.displayName"/>.
|
||||
///
|
||||
/// You can also use this property to read mouse buttons. For example:
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// Mouse.current.leftButton.wasPressedThisFrame
|
||||
/// Mouse.current.rightButton.wasPressedThisFrame
|
||||
/// Mouse.current.middleButton.wasPressedThisFrame
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
///
|
||||
/// </remarks>
|
||||
public bool wasPressedThisFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
// Take the old path if this is the first time calling.
|
||||
if (!needsToCheckFramePress)
|
||||
{
|
||||
var currentlyPressed = IsValueConsideredPressed(value);
|
||||
var pressedLastFrame = IsValueConsideredPressed(ReadValueFromPreviousFrame());
|
||||
BeginTestingForFramePresses(currentlyPressed, pressedLastFrame);
|
||||
|
||||
return device.wasUpdatedThisFrame && currentlyPressed && !pressedLastFrame;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (InputUpdate.s_LatestUpdateType.IsEditorUpdate())
|
||||
return InputUpdate.s_UpdateStepCount == m_UpdateCountLastPressedEditor;
|
||||
#endif
|
||||
return InputUpdate.s_UpdateStepCount == m_UpdateCountLastPressed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the press ended this frame.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// _Note_: The Input System identifies keys by physical layout, not according to the current language mapping of the keyboard. To query the name of the key according to the language mapping, use <see cref="InputControl.displayName"/>.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para>An example showing the use of this property on a gamepad button and a keyboard key:</para>
|
||||
///
|
||||
/// <code>
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.InputSystem;
|
||||
///
|
||||
/// public class ExampleScript : MonoBehaviour
|
||||
/// {
|
||||
/// void Update()
|
||||
/// {
|
||||
/// bool buttonReleased = Gamepad.current.aButton.wasReleasedThisFrame;
|
||||
/// bool spaceKeyReleased = Keyboard.current.spaceKey.wasReleasedThisFrame;
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool wasReleasedThisFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
// Take the old path if this is the first time calling.
|
||||
if (!needsToCheckFramePress)
|
||||
{
|
||||
var currentlyPressed = IsValueConsideredPressed(value);
|
||||
var pressedLastFrame = IsValueConsideredPressed(ReadValueFromPreviousFrame());
|
||||
BeginTestingForFramePresses(currentlyPressed, pressedLastFrame);
|
||||
|
||||
return device.wasUpdatedThisFrame && !currentlyPressed && pressedLastFrame;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (InputUpdate.s_LatestUpdateType.IsEditorUpdate())
|
||||
return InputUpdate.s_UpdateStepCount == m_UpdateCountLastReleasedEditor;
|
||||
#endif
|
||||
return InputUpdate.s_UpdateStepCount == m_UpdateCountLastReleased;
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateWasPressed()
|
||||
{
|
||||
var isNowPressed = IsValueConsideredPressed(value);
|
||||
|
||||
if (m_LastUpdateWasPress != isNowPressed)
|
||||
{
|
||||
if (isNowPressed)
|
||||
m_UpdateCountLastPressed = device.m_CurrentUpdateStepCount;
|
||||
else
|
||||
m_UpdateCountLastReleased = device.m_CurrentUpdateStepCount;
|
||||
|
||||
m_LastUpdateWasPress = isNowPressed;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal void UpdateWasPressedEditor()
|
||||
{
|
||||
var isNowPressed = IsValueConsideredPressed(value);
|
||||
|
||||
if (m_LastUpdateWasPressEditor != isNowPressed)
|
||||
{
|
||||
if (isNowPressed)
|
||||
m_UpdateCountLastPressedEditor = device.m_CurrentUpdateStepCount;
|
||||
else
|
||||
m_UpdateCountLastReleasedEditor = device.m_CurrentUpdateStepCount;
|
||||
|
||||
m_LastUpdateWasPressEditor = isNowPressed;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
// We make the current global default button press point available as a static so that we don't have to
|
||||
// constantly make the hop from InputSystem.settings -> InputManager.m_Settings -> defaultButtonPressPoint.
|
||||
internal static float s_GlobalDefaultButtonPressPoint;
|
||||
internal static float s_GlobalDefaultButtonReleaseThreshold;
|
||||
|
||||
// We clamp button press points to this value as allowing 0 as the press point causes all buttons
|
||||
// to implicitly be pressed all the time. Not useful.
|
||||
internal const float kMinButtonPressPoint = 0.0001f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d618f7e3781548c28836f39ae65d95e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,207 @@
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of common usage string values as reported by <see cref="InputControl.usages"/>.
|
||||
/// </summary>
|
||||
public static class CommonUsages
|
||||
{
|
||||
/// <summary>
|
||||
/// Primary 2D motion control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Left stick on a gamepad.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Primary2DMotion = new InternedString("Primary2DMotion");
|
||||
|
||||
/// <summary>
|
||||
/// Secondary 2D motion control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Right stick on a gamepad.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Secondary2DMotion = new InternedString("Secondary2DMotion");
|
||||
|
||||
/// <summary>
|
||||
/// The primary action control on any input device, such as a gamepad, mouse, or keyboard.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Primary mouse button (left button on right-handed configuration, right button on left-handed configuration),
|
||||
/// south-button on a gamepad.
|
||||
/// </remarks>
|
||||
public static readonly InternedString PrimaryAction = new InternedString("PrimaryAction");
|
||||
|
||||
/// <summary>
|
||||
/// Secondary action control on any input device, such as a gamepad, mouse, or keyboard.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Secondary mouse button (right button on right-handed configuration, left button on left-handed configuration),
|
||||
/// east-button on a gamepad.
|
||||
/// </remarks>
|
||||
public static readonly InternedString SecondaryAction = new InternedString("SecondaryAction");
|
||||
|
||||
/// <summary>
|
||||
/// The primary trigger control on input devices with triggers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Right trigger-button on a gamepad.
|
||||
/// </remarks>
|
||||
public static readonly InternedString PrimaryTrigger = new InternedString("PrimaryTrigger");
|
||||
|
||||
/// <summary>
|
||||
/// The secondary trigger control on input devices with triggers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Left trigger-button on a gamepad.
|
||||
/// </remarks>
|
||||
public static readonly InternedString SecondaryTrigger = new InternedString("SecondaryTrigger");
|
||||
|
||||
/// <summary>
|
||||
/// A modifier action control that modifies usage of other controls.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Keyboard modifier keys like CTRL, SHIFT, ALT, OPTION, etc.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Modifier = new InternedString("Modifier");
|
||||
|
||||
/// <summary>
|
||||
/// The spatial position control on input devices with spatial tracking.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: User head position in tracking-space using e.g. a head-tracking system. This could for example be a VR tracking system or another user-facing tracking sensor.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Position = new InternedString("Position");
|
||||
|
||||
/// <summary>
|
||||
/// The spatial orientation control on input devices with spatial tracking.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: User head-orientation in tracking-space using e.g. a head-tracking system. This could for example be a VR tracking system or another user-facing tracking sensor.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Orientation = new InternedString("Orientation");
|
||||
|
||||
/// <summary>
|
||||
/// The primary hat-switch control on input devices with hat-switches such as joysticks or gamepads.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Joystick or gamepad hat-switch.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Hatswitch = new InternedString("Hatswitch");
|
||||
|
||||
/// <summary>
|
||||
/// Button to navigate to previous location.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Escape on keyboard, B button on gamepad.
|
||||
///
|
||||
/// In general, the "Back" control is used for moving backwards in the navigation history
|
||||
/// of a UI. This is used, for example, in hierarchical menu structures to move back to parent menus
|
||||
/// (e.g. from the "Settings" menu back to the "Main" menu). Consoles generally have stringent requirements
|
||||
/// as to which button has to fulfill this role.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Back = new InternedString("Back");
|
||||
|
||||
/// <summary>
|
||||
/// Button to navigate to next location.
|
||||
/// </summary>
|
||||
public static readonly InternedString Forward = new InternedString("Forward");
|
||||
|
||||
/// <summary>
|
||||
/// Button to bring up menu.
|
||||
/// </summary>
|
||||
public static readonly InternedString Menu = new InternedString("Menu");
|
||||
|
||||
/// <summary>
|
||||
/// Button to confirm the current choice.
|
||||
/// </summary>
|
||||
public static readonly InternedString Submit = new InternedString("Submit");
|
||||
|
||||
////REVIEW: isn't this the same as "Back"?
|
||||
/// <summary>
|
||||
/// Button to not accept the current choice.
|
||||
/// </summary>
|
||||
public static readonly InternedString Cancel = new InternedString("Cancel");
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal motion axis.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: X axis on mouse.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Horizontal = new InternedString("Horizontal");
|
||||
|
||||
/// <summary>
|
||||
/// Vertical motion axis.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Y axis on mouse.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Vertical = new InternedString("Vertical");
|
||||
|
||||
/// <summary>
|
||||
/// Rotation around single, fixed axis.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: twist on joystick or twist of pen (few pens support that).
|
||||
/// </remarks>
|
||||
public static readonly InternedString Twist = new InternedString("Twist");
|
||||
|
||||
/// <summary>
|
||||
/// Pressure level axis.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: pen pressure.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Pressure = new InternedString("Pressure");
|
||||
|
||||
/// <summary>
|
||||
/// Axis to scroll horizontally.
|
||||
/// </summary>
|
||||
public static readonly InternedString ScrollHorizontal = new InternedString("ScrollHorizontal");
|
||||
|
||||
/// <summary>
|
||||
/// Axis to scroll vertically.
|
||||
/// </summary>
|
||||
public static readonly InternedString ScrollVertical = new InternedString("ScrollVertical");
|
||||
|
||||
/// <summary>
|
||||
/// A screen-space point.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: Touch contact point.
|
||||
/// </remarks>
|
||||
public static readonly InternedString Point = new InternedString("Point");
|
||||
|
||||
/// <summary>
|
||||
/// Low-frequency haptic motor for force-feedback.
|
||||
/// </summary>
|
||||
public static readonly InternedString LowFreqMotor = new InternedString("LowFreqMotor");
|
||||
|
||||
/// <summary>
|
||||
/// High-frequency haptic motor for force-feedback.
|
||||
/// </summary>
|
||||
public static readonly InternedString HighFreqMotor = new InternedString("HighFreqMotor");
|
||||
|
||||
/// <summary>
|
||||
/// Device in left hand.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: left hand XR controller.
|
||||
/// </remarks>
|
||||
public static readonly InternedString LeftHand = new InternedString("LeftHand");
|
||||
|
||||
/// <summary>
|
||||
/// Device in right hand.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: right hand XR controller.
|
||||
/// </remarks>
|
||||
public static readonly InternedString RightHand = new InternedString("RightHand");
|
||||
|
||||
/// <summary>
|
||||
/// Axis representing charge of battery (1=full, 0=empty).
|
||||
/// </summary>
|
||||
public static readonly InternedString BatteryStrength = new InternedString("BatteryStrength");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf624ae4ff5e44cebdcba54ae1ff380a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,70 @@
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Delta controls are a two-dimensional motion vector that accumulate within a frame
|
||||
/// and reset at the beginning of a frame. You can read the values from a delta control
|
||||
/// using the inherited members from Vector2Control or InputControl.
|
||||
/// </summary>
|
||||
/// <see cref="Pointer.delta"/>
|
||||
/// <seealso cref="Mouse.scroll"/>
|
||||
[Preserve]
|
||||
public class DeltaControl : Vector2Control
|
||||
{
|
||||
/// <summary>
|
||||
/// A synthetic axis representing the upper half of the Y axis value, i.e. the 0 to 1 range.
|
||||
/// </summary>
|
||||
/// <value>Control representing the control's upper half Y axis.</value>
|
||||
/// <remarks>
|
||||
/// The control is marked as <see cref="InputControl.synthetic"/>.
|
||||
/// </remarks>
|
||||
[InputControl(useStateFrom = "y", parameters = "clamp=1,clampMin=0,clampMax=3.402823E+38", synthetic = true, displayName = "Up")]
|
||||
[Preserve]
|
||||
public AxisControl up { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A synthetic axis representing the lower half of the Y axis value, i.e. the 0 to -1 range (inverted).
|
||||
/// </summary>
|
||||
/// <value>Control representing the control's lower half Y axis.</value>
|
||||
/// <remarks>
|
||||
/// The control is marked as <see cref="InputControl.synthetic"/>.
|
||||
/// </remarks>
|
||||
[InputControl(useStateFrom = "y", parameters = "clamp=1,clampMin=-3.402823E+38,clampMax=0,invert", synthetic = true, displayName = "Down")]
|
||||
[Preserve]
|
||||
public AxisControl down { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A synthetic axis representing the left half of the X axis value, i.e. the 0 to -1 range (inverted).
|
||||
/// </summary>
|
||||
/// <value>Control representing the control's left half X axis.</value>
|
||||
/// <remarks>
|
||||
/// The control is marked as <see cref="InputControl.synthetic"/>.
|
||||
/// </remarks>
|
||||
[InputControl(useStateFrom = "x", parameters = "clamp=1,clampMin=-3.402823E+38,clampMax=0,invert", synthetic = true, displayName = "Left")]
|
||||
[Preserve]
|
||||
public AxisControl left { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A synthetic axis representing the right half of the X axis value, i.e. the 0 to 1 range.
|
||||
/// </summary>
|
||||
/// <value>Control representing the control's right half X axis.</value>
|
||||
/// <remarks>
|
||||
/// The control is marked as <see cref="InputControl.synthetic"/>.
|
||||
/// </remarks>
|
||||
[InputControl(useStateFrom = "x", parameters = "clamp=1,clampMin=0,clampMax=3.402823E+38", synthetic = true, displayName = "Right")]
|
||||
[Preserve]
|
||||
public AxisControl right { get; set; }
|
||||
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
|
||||
up = GetChildControl<AxisControl>("up");
|
||||
down = GetChildControl<AxisControl>("down");
|
||||
left = GetChildControl<AxisControl>("left");
|
||||
right = GetChildControl<AxisControl>("right");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 339c9c6bc0554ad79681e127d417ef3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: hide in UI
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A button that is considered pressed if the underlying state has a value in the specific range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This control is most useful for handling HID-style hat switches. Unlike <see cref="DpadControl"/>,
|
||||
/// which by default is stored as a bitfield of four bits that each represent a direction on the pad,
|
||||
/// these hat switches enumerate the possible directions that the switch can be moved in. For example,
|
||||
/// the value 1 could indicate that the switch is moved to the left whereas 3 could indicate it is
|
||||
/// moved up.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [StructLayout(LayoutKind.Explicit, Size = 32)]
|
||||
/// internal struct DualShock4HIDInputReport : IInputStateTypeInfo
|
||||
/// {
|
||||
/// [FieldOffset(0)] public byte reportId;
|
||||
///
|
||||
/// [InputControl(name = "dpad", format = "BIT", layout = "Dpad", sizeInBits = 4, defaultState = 8)]
|
||||
/// [InputControl(name = "dpad/up", format = "BIT", layout = "DiscreteButton", parameters = "minValue=7,maxValue=1,nullValue=8,wrapAtValue=7", bit = 0, sizeInBits = 4)]
|
||||
/// [InputControl(name = "dpad/right", format = "BIT", layout = "DiscreteButton", parameters = "minValue=1,maxValue=3", bit = 0, sizeInBits = 4)]
|
||||
/// [InputControl(name = "dpad/down", format = "BIT", layout = "DiscreteButton", parameters = "minValue=3,maxValue=5", bit = 0, sizeInBits = 4)]
|
||||
/// [InputControl(name = "dpad/left", format = "BIT", layout = "DiscreteButton", parameters = "minValue=5, maxValue=7", bit = 0, sizeInBits = 4)]
|
||||
/// [FieldOffset(5)] public byte buttons1;
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public class DiscreteButtonControl : ButtonControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Value (inclusive) at which to start considering the button to be pressed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="minValue"/> is allowed to be larger than <see cref="maxValue"/>. This indicates
|
||||
/// a setup where the value wraps around beyond <see cref="minValue"/>, skips <see cref="nullValue"/>,
|
||||
/// and then goes all the way up to <see cref="maxValue"/>.
|
||||
///
|
||||
/// For example, if the underlying state represents a circular D-pad and enumerates its
|
||||
/// 9 possible positions (including null state) going clock-wise from 0 to 8 and with 1
|
||||
/// indicating that the D-pad is pressed to the left, then 1, 2, and 8 would indicate
|
||||
/// that the "left" button is held on the D-pad. To set this up, set <see cref="minValue"/>
|
||||
/// to 8, <see cref="maxValue"/> to 2, and <see cref="nullValue"/> to 0 (the default).
|
||||
/// </remarks>
|
||||
public int minValue;
|
||||
|
||||
/// <summary>
|
||||
/// Value (inclusive) beyond which to stop considering the button to be pressed.
|
||||
/// </summary>
|
||||
public int maxValue;
|
||||
|
||||
/// <summary>
|
||||
/// Value (inclusive) at which the values cut off and wrap back around. Considered to not be set if equal to <see cref="nullValue"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful if, for example, an enumeration has more bits than are used for "on" states of controls. For example,
|
||||
/// a bitfield of 4 bits where values 1-7 (i.e. 0001 to 0111) indicate "on" states of controls and value 8 indicating an
|
||||
/// "off" state. In that case, set <see cref="nullValue"/> to 8 and <see cref="wrapAtValue"/> to 7.
|
||||
/// </remarks>
|
||||
public int wrapAtValue;
|
||||
|
||||
/// <summary>
|
||||
/// Value at which the button is considered to not be pressed. Usually zero. Some devices, however,
|
||||
/// use non-zero default states.
|
||||
/// </summary>
|
||||
public int nullValue;
|
||||
|
||||
/// <summary>
|
||||
/// Determines the behavior of <see cref="WriteValueIntoState"/>. By default, attempting to write a <see cref="DiscreteButtonControl"/>
|
||||
/// will result in a <c>NotSupportedException</c>.
|
||||
/// </summary>
|
||||
public WriteMode writeMode;
|
||||
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
|
||||
if (!stateBlock.format.IsIntegerFormat())
|
||||
throw new NotSupportedException(
|
||||
$"Non-integer format '{stateBlock.format}' is not supported for DiscreteButtonControl '{this}'");
|
||||
}
|
||||
|
||||
public override unsafe float ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
var valuePtr = (byte*)statePtr + (int)m_StateBlock.byteOffset;
|
||||
// Note that all signed data in state buffers is in excess-K format.
|
||||
var intValue = MemoryHelpers.ReadTwosComplementMultipleBitsAsInt(valuePtr, m_StateBlock.bitOffset, m_StateBlock.sizeInBits);
|
||||
|
||||
var value = 0.0f;
|
||||
if (minValue > maxValue)
|
||||
{
|
||||
// If no wrapping point is set, default to wrapping around exactly
|
||||
// at the point of minValue.
|
||||
if (wrapAtValue == nullValue)
|
||||
wrapAtValue = minValue;
|
||||
|
||||
if ((intValue >= minValue && intValue <= wrapAtValue)
|
||||
|| (intValue != nullValue && intValue <= maxValue))
|
||||
value = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = intValue >= minValue && intValue <= maxValue ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
return Preprocess(value);
|
||||
}
|
||||
|
||||
public override unsafe void WriteValueIntoState(float value, void* statePtr)
|
||||
{
|
||||
if (writeMode == WriteMode.WriteNullAndMaxValue)
|
||||
{
|
||||
var valuePtr = (byte*)statePtr + (int)m_StateBlock.byteOffset;
|
||||
var valueToWrite = value >= pressPointOrDefault ? maxValue : nullValue;
|
||||
MemoryHelpers.WriteIntAsTwosComplementMultipleBits(valuePtr, m_StateBlock.bitOffset, m_StateBlock.sizeInBits, valueToWrite);
|
||||
return;
|
||||
}
|
||||
|
||||
// The way these controls are usually used, the state is shared between multiple DiscreteButtons. So writing one
|
||||
// may have unpredictable effects on the value of other buttons.
|
||||
throw new NotSupportedException("Writing value states for DiscreteButtonControl is not supported as a single value may correspond to multiple states");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How <see cref="DiscreteButtonControl.WriteValueIntoState"/> should behave.
|
||||
/// </summary>
|
||||
public enum WriteMode
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="DiscreteButtonControl.WriteValueIntoState"/> will throw <c>NotSupportedException</c>.
|
||||
/// </summary>
|
||||
WriteDisabled = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Write <see cref="DiscreteButtonControl.nullValue"/> for when the button is considered not pressed and
|
||||
/// write <see cref="DiscreteButtonControl.maxValue"/> for when the button is considered pressed.
|
||||
/// </summary>
|
||||
WriteNullAndMaxValue = 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ab470181bfe4ba891e0d1584730fc4c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A control reading a <see cref="double"/>.
|
||||
/// </summary>
|
||||
public class DoubleControl : InputControl<double>
|
||||
{
|
||||
/// <summary>
|
||||
/// Default-initialize the control.
|
||||
/// </summary>
|
||||
public DoubleControl()
|
||||
{
|
||||
m_StateBlock.format = InputStateBlock.FormatDouble;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override unsafe double ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
return m_StateBlock.ReadDouble(statePtr);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override unsafe void WriteValueIntoState(double value, void* statePtr)
|
||||
{
|
||||
m_StateBlock.WriteDouble(statePtr, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72251f49d44478b4c93462938d7ce4cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A control made up of four discrete, directional buttons. Forms a vector
|
||||
/// but can also be addressed as individual buttons.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Is stored as four bits by default.
|
||||
///
|
||||
/// The vector that is aggregated from the button states is normalized. I.e.
|
||||
/// even if pressing diagonally, the vector will have a length of 1 (instead
|
||||
/// of reading something like <c>(1,1)</c> for example).
|
||||
/// </remarks>
|
||||
public class DpadControl : Vector2Control
|
||||
{
|
||||
[InputControlLayout(hideInUI = true)]
|
||||
public class DpadAxisControl : AxisControl
|
||||
{
|
||||
public int component { get; set; }
|
||||
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
component = name == "x" ? 0 : 1;
|
||||
|
||||
// Set the state block to be the parent's state block. We don't use that to read
|
||||
// the axis directly (we call the parent control to do that), but we need to set
|
||||
// it up the actions know to monitor this memory for changes to the control.
|
||||
m_StateBlock = m_Parent.m_StateBlock;
|
||||
}
|
||||
|
||||
public override unsafe float ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
var value = ((DpadControl)m_Parent).ReadUnprocessedValueFromState(statePtr);
|
||||
return value[component];
|
||||
}
|
||||
}
|
||||
|
||||
// The DpadAxisControl has it's own logic to read state from the parent dpad.
|
||||
// The useStateFrom argument here is not actually used by that. The only reason
|
||||
// it is set up here is to avoid any state bytes being reserved for the DpadAxisControl.
|
||||
[InputControl(name = "x", layout = "DpadAxis", useStateFrom = "right", synthetic = true)]
|
||||
[InputControl(name = "y", layout = "DpadAxis", useStateFrom = "up", synthetic = true)]
|
||||
|
||||
/// <summary>
|
||||
/// The button representing the vertical upwards state of the D-Pad.
|
||||
/// </summary>
|
||||
[InputControl(bit = (int)ButtonBits.Up, displayName = "Up")]
|
||||
public ButtonControl up { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The button representing the vertical downwards state of the D-Pad.
|
||||
/// </summary>
|
||||
[InputControl(bit = (int)ButtonBits.Down, displayName = "Down")]
|
||||
public ButtonControl down { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The button representing the horizontal left state of the D-Pad.
|
||||
/// </summary>
|
||||
[InputControl(bit = (int)ButtonBits.Left, displayName = "Left")]
|
||||
public ButtonControl left { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The button representing the horizontal right state of the D-Pad.
|
||||
/// </summary>
|
||||
[InputControl(bit = (int)ButtonBits.Right, displayName = "Right")]
|
||||
public ButtonControl right { get; set; }
|
||||
|
||||
////TODO: should have X and Y child controls as well
|
||||
|
||||
public DpadControl()
|
||||
{
|
||||
m_StateBlock.sizeInBits = 4;
|
||||
m_StateBlock.format = InputStateBlock.FormatBit;
|
||||
}
|
||||
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
up = GetChildControl<ButtonControl>("up");
|
||||
down = GetChildControl<ButtonControl>("down");
|
||||
left = GetChildControl<ButtonControl>("left");
|
||||
right = GetChildControl<ButtonControl>("right");
|
||||
base.FinishSetup();
|
||||
}
|
||||
|
||||
public override unsafe Vector2 ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
var upIsPressed = up.ReadValueFromStateWithCaching(statePtr) >= up.pressPointOrDefault;
|
||||
var downIsPressed = down.ReadValueFromStateWithCaching(statePtr) >= down.pressPointOrDefault;
|
||||
var leftIsPressed = left.ReadValueFromStateWithCaching(statePtr) >= left.pressPointOrDefault;
|
||||
var rightIsPressed = right.ReadValueFromStateWithCaching(statePtr) >= right.pressPointOrDefault;
|
||||
|
||||
return MakeDpadVector(upIsPressed, downIsPressed, leftIsPressed, rightIsPressed);
|
||||
}
|
||||
|
||||
public override unsafe void WriteValueIntoState(Vector2 value, void* statePtr)
|
||||
{
|
||||
var upIsPressed = up.IsValueConsideredPressed(value.y);
|
||||
var downIsPressed = down.IsValueConsideredPressed(value.y * -1f);
|
||||
var leftIsPressed = left.IsValueConsideredPressed(value.x * -1f);
|
||||
var rightIsPressed = right.IsValueConsideredPressed(value.x);
|
||||
|
||||
up.WriteValueIntoState(upIsPressed && !downIsPressed ? value.y : 0f, statePtr);
|
||||
down.WriteValueIntoState(downIsPressed && !upIsPressed ? value.y * -1f : 0f, statePtr);
|
||||
left.WriteValueIntoState(leftIsPressed && !rightIsPressed ? value.x * -1f : 0f, statePtr);
|
||||
right.WriteValueIntoState(rightIsPressed && !leftIsPressed ? value.x : 0f, statePtr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a direction vector from the given four button states.
|
||||
/// </summary>
|
||||
/// <param name="up">Whether button representing the up direction is pressed.</param>
|
||||
/// <param name="down">Whether button representing the down direction is pressed.</param>
|
||||
/// <param name="left">Whether button representing the left direction is pressed.</param>
|
||||
/// <param name="right">Whether button representing the right direction is pressed.</param>
|
||||
/// <param name="normalize">Whether to normalize the resulting vector. If this is false, vectors in the diagonal
|
||||
/// directions will have a magnitude of greater than 1. For example, up-left will be (-1,1).</param>
|
||||
/// <returns>A 2D direction vector.</returns>
|
||||
public static Vector2 MakeDpadVector(bool up, bool down, bool left, bool right, bool normalize = true)
|
||||
{
|
||||
var upValue = up ? 1.0f : 0.0f;
|
||||
var downValue = down ? -1.0f : 0.0f;
|
||||
var leftValue = left ? -1.0f : 0.0f;
|
||||
var rightValue = right ? 1.0f : 0.0f;
|
||||
|
||||
var result = new Vector2(leftValue + rightValue, upValue + downValue);
|
||||
|
||||
if (normalize)
|
||||
{
|
||||
// If press is diagonal, adjust coordinates to produce vector of length 1.
|
||||
// pow(0.707107) is roughly 0.5 so sqrt(pow(0.707107)+pow(0.707107)) is ~1.
|
||||
const float diagonal = 0.707107f;
|
||||
if (result.x != 0 && result.y != 0)
|
||||
result = new Vector2(result.x * diagonal, result.y * diagonal);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a direction vector from the given axis states.
|
||||
/// </summary>
|
||||
/// <param name="up">Axis value representing the up direction.</param>
|
||||
/// <param name="down">Axis value representing the down direction.</param>
|
||||
/// <param name="left">Axis value representing the left direction.</param>
|
||||
/// <param name="right">Axis value representing the right direction.</param>
|
||||
/// <returns>A 2D direction vector.</returns>
|
||||
public static Vector2 MakeDpadVector(float up, float down, float left, float right)
|
||||
{
|
||||
return new Vector2(-left + right, up - down);
|
||||
}
|
||||
|
||||
internal enum ButtonBits
|
||||
{
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b00a605ae58d438baf0b80388b377972
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1807
Packages/com.unity.inputsystem/InputSystem/Controls/InputControl.cs
Normal file
1807
Packages/com.unity.inputsystem/InputSystem/Controls/InputControl.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d59a0dc8508f4cf1819f6ba99250cd8e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,352 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.Composites;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
namespace UnityEngine.InputSystem.Layouts
|
||||
{
|
||||
/// <summary>
|
||||
/// Mark a field or property as representing/identifying an input control in some form.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This attribute is used in different places for different purposes.
|
||||
///
|
||||
/// When creating input control layouts (<see cref="InputControlLayout"/>) in C#, applying the
|
||||
/// attribute to fields in a state struct (see <see cref="IInputStateTypeInfo"/> or <see cref="GamepadState"/>
|
||||
/// for an example) or to properties in an input device (<see cref="InputDevice"/>), will cause an
|
||||
/// <see cref="InputControl"/> to be created from the field or property at runtime. The attribute
|
||||
/// can be applied multiple times to create multiple input controls (e.g. when having an int field
|
||||
/// that represents a bitfield where each bit is a separate button).
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public class MyDevice : InputDevice
|
||||
/// {
|
||||
/// // Adds an InputControl with name=myButton and layout=Button to the device.
|
||||
/// [InputControl]
|
||||
/// public ButtonControl myButton { get; set; }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// Another use is for marking <c>string</c> type fields that represent input control paths. Applying
|
||||
/// the attribute to them will cause them to automatically use a custom inspector similar to the one
|
||||
/// found in the action editor. For this use, only the <see cref="layout"/> property is taken into
|
||||
/// account.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public class MyBehavior : MonoBehaviour
|
||||
/// {
|
||||
/// // In the inspector, shows a control selector that is restricted to
|
||||
/// // selecting buttons. As a result, controlPath will be set to path
|
||||
/// // representing the control that was picked (e.g. "<Gamepad>/buttonSouth").
|
||||
/// [InputControl(layout = "Button")]
|
||||
/// public string controlPath;
|
||||
///
|
||||
/// protected void OnEnable()
|
||||
/// {
|
||||
/// // Find controls by path.
|
||||
/// var controls = InputSystem.FindControl(controlPath);
|
||||
/// //...
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// Finally, the attribute is also used in composite bindings (<see cref="InputBindingComposite"/>)
|
||||
/// to mark fields that reference parts of the composite. An example for this is <see cref="AxisComposite.negative"/>.
|
||||
/// In this use, also only the <see cref="layout"/> property is taken into account while other properties
|
||||
/// are ignored.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public class MyComposite : InputBindingComposite<float>
|
||||
/// {
|
||||
/// // Add a part to the composite called 'firstControl' which expects
|
||||
/// // AxisControls.
|
||||
/// [InputControl(layout = "Axis")]
|
||||
/// public int firstControl;
|
||||
///
|
||||
/// // Add a part to the composite called 'secondControl' which expects
|
||||
/// // Vector3Controls.
|
||||
/// [InputControl(layout = "Vector3")]
|
||||
/// public int secondControl;
|
||||
///
|
||||
/// //...
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControlLayout"/>
|
||||
/// <seealso cref="InputBindingComposite"/>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]
|
||||
public sealed class InputControlAttribute : PropertyAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Layout to use for the control.
|
||||
/// </summary>
|
||||
/// <value>Layout to use for the control.</value>
|
||||
/// <remarks>
|
||||
/// If this is not set, the system tries to infer the layout type from the value type of
|
||||
/// the field or property. If the value type is itself registered as a layout, that layout
|
||||
/// will be used (e.g. when you have a property of type <see cref="Controls.ButtonControl"/>, the layout
|
||||
/// will be inferred to be "Button"). Otherwise, if a layout with the same name as the type is registered,
|
||||
/// that layout will be used (e.g. when you have a field of type <see cref="Vector3"/>, the layout
|
||||
/// will be inferred to be "Vector3").
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControlLayout"/>
|
||||
public string layout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Layout variant to use for the control.
|
||||
/// </summary>
|
||||
/// <value>Layout variant to use for the control.</value>
|
||||
public string variants { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name to give to the name. If null or empty, the name of the property or
|
||||
/// field the attribute is applied to will be used.
|
||||
/// </summary>
|
||||
/// <value>Name to give to the control.</value>
|
||||
/// <seealso cref="InputControl.name"/>
|
||||
public string name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Storage format to use for the control. If not set, default storage format
|
||||
/// for the given <see cref="layout"/> is used.
|
||||
/// </summary>
|
||||
/// <value>Memory storage format to use for the control.</value>
|
||||
/// <seealso cref="InputStateBlock.format"/>
|
||||
public string format { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Usage to apply to the control.
|
||||
/// </summary>
|
||||
/// <value>Usage for the control.</value>
|
||||
/// <remarks>
|
||||
/// This property can be used in place of <see cref="usages"/> to set just a single
|
||||
/// usage on the control.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControl.usages"/>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.usages"/>
|
||||
/// <seealso cref="CommonUsages"/>
|
||||
public string usage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Usages to apply to the control.
|
||||
/// </summary>
|
||||
/// <value>Usages for the control.</value>
|
||||
/// <remarks>
|
||||
/// This property should be used instead of <see cref="usage"/> when a control has multiple usages.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControl.usages"/>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.usages"/>
|
||||
/// <seealso cref="CommonUsages"/>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "According to MSDN, this message can be ignored for attribute parameters, as there are no better alternatives.")]
|
||||
public string[] usages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional list of parameters to apply to the control.
|
||||
/// </summary>
|
||||
/// <value>Parameters to apply to the control.</value>
|
||||
/// <remarks>
|
||||
/// An <see cref="InputControl"/> may expose public fields which can be set as
|
||||
/// parameters. An example of this is <see cref="Controls.AxisControl.clamp"/>.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public struct MyStateStruct : IInputStateTypeInfo
|
||||
/// {
|
||||
/// [InputControl(parameters = "clamp,clampMin=-0.5,clampMax=0.5")]
|
||||
/// public float axis;
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.parameters"/>
|
||||
public string parameters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional list of processors to add to the control.
|
||||
/// </summary>
|
||||
/// <value>Processors to apply to the control.</value>
|
||||
/// <remarks>
|
||||
/// Each element in the list is a name of a processor (as registered with
|
||||
/// <see cref="InputSystem.RegisterProcessor{T}"/>) followed by an optional
|
||||
/// list of parameters.
|
||||
///
|
||||
/// For example, <c>"normalize(min=0,max=256)"</c> is one element that puts
|
||||
/// a <c>NormalizeProcessor</c> on the control and sets its <c>min</c> field
|
||||
/// to 0 and its its <c>max</c> field to 256.
|
||||
///
|
||||
/// Multiple processors can be put on a control by separating them with a comma.
|
||||
/// For example, <c>"normalize(max=256),scale(factor=2)"</c> puts both a <c>NormalizeProcessor</c>
|
||||
/// and a <c>ScaleProcessor</c> on the control. Processors are applied in the
|
||||
/// order they are listed.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.processors"/>
|
||||
/// <seealso cref="InputBinding.processors"/>
|
||||
public string processors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An alternative name that can be used in place of <see cref="name"/> to find
|
||||
/// the control.
|
||||
/// </summary>
|
||||
/// <value>Alternative name for the control.</value>
|
||||
/// <remarks>
|
||||
/// This property can be used instead of <see cref="aliases"/> when there is only a
|
||||
/// single alias for the control.
|
||||
///
|
||||
/// Aliases, like names, are case-insensitive. Any control may have arbitrary many
|
||||
/// aliases.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControl.aliases"/>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.aliases"/>
|
||||
public string alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of alternative names that can be used in place of <see cref="name"/> to
|
||||
/// find the control.
|
||||
/// </summary>
|
||||
/// <value>Alternative names for the control.</value>
|
||||
/// <remarks>
|
||||
/// This property should be used instead of <see cref="alias"/> when a control has
|
||||
/// multiple aliases.
|
||||
///
|
||||
/// Aliases, like names, are case-insensitive. Any control may have arbitrary many
|
||||
/// aliases.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControl.aliases"/>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.aliases"/>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "According to MSDN, this message can be ignored for attribute parameters, as there are no better alternatives.")]
|
||||
public string[] aliases { get; set; }
|
||||
|
||||
public string useStateFrom { get; set; }
|
||||
|
||||
public uint bit { get; set; } = InputStateBlock.InvalidOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Offset in bytes to where the memory of the control starts. Relative to
|
||||
/// the offset of the parent control (which may be the device itself).
|
||||
/// </summary>
|
||||
/// <value>Byte offset of the control.</value>
|
||||
/// <remarks>
|
||||
/// If the attribute is applied to fields in an <see cref="InputControlLayout"/> and
|
||||
/// this property is not set, the offset of the field is used instead.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public struct MyStateStruct : IInputStateTypeInfo
|
||||
/// {
|
||||
/// public int buttons;
|
||||
///
|
||||
/// [InputControl] // Automatically uses the offset of 'axis'.
|
||||
/// public float axis;
|
||||
/// }
|
||||
///
|
||||
/// [InputControlLayout(stateType = typeof(MyStateStruct))]
|
||||
/// public class MyDevice : InputDevice
|
||||
/// {
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.offset"/>
|
||||
public uint offset { get; set; } = InputStateBlock.InvalidOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Size of the memory storage for the control in bits.
|
||||
/// </summary>
|
||||
/// <value>Size of the control in bits.</value>
|
||||
/// <remarks>
|
||||
/// If the attribute is applied to fields in an <see cref="InputControlLayout"/> and
|
||||
/// this property is not set, the size is taken from the field.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public struct MyStateStruct : IInputStateTypeInfo
|
||||
/// {
|
||||
/// public int buttons;
|
||||
///
|
||||
/// [InputControl] // Automatically uses sizeof(float).
|
||||
/// public float axis;
|
||||
/// }
|
||||
///
|
||||
/// [InputControlLayout(stateType = typeof(MyStateStruct))]
|
||||
/// public class MyDevice : InputDevice
|
||||
/// {
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.sizeInBits"/>
|
||||
/// <seealso cref="InputStateBlock.sizeInBits"/>
|
||||
public uint sizeInBits { get; set; }
|
||||
|
||||
public int arraySize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Display name to assign to the control.
|
||||
/// </summary>
|
||||
/// <value>Display name for the control.</value>
|
||||
/// <seealso cref="InputControl.displayName"/>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.displayName"/>
|
||||
public string displayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Short display name to assign to the control.
|
||||
/// </summary>
|
||||
/// <value>Short display name for the control.</value>
|
||||
/// <seealso cref="InputControl.shortDisplayName"/>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.shortDisplayName"/>
|
||||
public string shortDisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves whether the control is [noisy](xref:input-system-controls#noisy-controls). Off by default.
|
||||
/// </summary>
|
||||
/// <value>Whether control is noisy.</value>
|
||||
/// <seealso cref="InputControl.noisy"/>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.isNoisy"/>
|
||||
public bool noisy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves whether the control is [synthetic](xref:input-system-controls#synthetic-controls). Off by default.
|
||||
/// </summary>
|
||||
/// <value>Whether control is synthetic.</value>
|
||||
/// <seealso cref="InputControl.synthetic"/>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.isSynthetic"/>
|
||||
public bool synthetic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to specify that a control should not be reset when its device is reset.
|
||||
/// </summary>
|
||||
/// <value>If true, resets of the device will leave the value of the control untouched except if a "hard" reset
|
||||
/// is explicitly enforced.</value>
|
||||
/// <seealso cref="InputSystem.ResetDevice"/>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.dontReset"/>
|
||||
public bool dontReset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default state to write into the control's memory.
|
||||
/// </summary>
|
||||
/// <value>Default memory state for the control.</value>
|
||||
/// <remarks>
|
||||
/// This is not the default <em>value</em> but rather the default memory state, i.e.
|
||||
/// the raw memory value read and the processed and returned as a value. By default
|
||||
/// this is <c>null</c> and result in a control's memory to be initialized with all
|
||||
/// zeroes.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.defaultState"/>
|
||||
public object defaultState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lower limit for values of the control.
|
||||
/// </summary>
|
||||
/// <value>Lower limit for values of the control.</value>
|
||||
/// <remarks>
|
||||
/// This is null by default in which case no lower bound is applied to the TODO
|
||||
/// </remarks>
|
||||
public object minValue { get; set; }
|
||||
public object maxValue { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f07d0a558a2d4f5c885936a3a1219008
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8cfbe75f80984c1d9700ef41f6e2aeb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a2515c58b39ea24c8154ebc84f7c157
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
////REVIEW: should this *not* be inherited? inheritance can lead to surprises
|
||||
|
||||
namespace UnityEngine.InputSystem.Layouts
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute to control layout settings of a type used to generate an <see cref="InputControlLayout"/>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class InputControlLayoutAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Associates a state representation with an input device and drives
|
||||
/// the control layout generated for the device from its state rather
|
||||
/// than from the device class.
|
||||
/// </summary>
|
||||
/// <remarks>This is *only* useful if you have a state struct dictating a specific
|
||||
/// state layout and you want the device layout to automatically take offsets from
|
||||
/// the fields annotated with <see cref="InputControlAttribute"/>.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public struct MyStateStruct : IInputStateTypeInfo
|
||||
/// {
|
||||
/// public FourCC format => new FourCC('M', 'Y', 'D', 'V');
|
||||
///
|
||||
/// [InputControl(name = "button1", layout = "Button", bit = 0)]
|
||||
/// [InputControl(name = "button2", layout = "Button", bit = 0)]
|
||||
/// public int buttons;
|
||||
/// }
|
||||
///
|
||||
/// [InputControlLayout(stateType = typeof(MyStateStruct)]
|
||||
/// public class MyDevice : InputDevice
|
||||
/// {
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="LowLevel.InputStateBlock"/>
|
||||
/// <seealso cref="LowLevel.MouseState"/>
|
||||
public Type stateType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="FourCC"/> identifier for the memory format associated with the layout.
|
||||
/// </summary>
|
||||
/// <seealso cref="LowLevel.InputStateBlock.format"/>
|
||||
public string stateFormat { get; set; }
|
||||
|
||||
////TODO: rename this to just "usages"; "commonUsages" is such a weird name
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "According to MSDN, this message can be ignored for attribute parameters, as there are no better alternatives.")]
|
||||
public string[] commonUsages { get; set; }
|
||||
|
||||
public string variants { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows marking a device as noisy regardless of control layout.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Controls can be individually marked as noisy using the <see cref="InputControlAttribute.noisy"/>
|
||||
/// attribute, but this property can be used to mark a device as noisy even when no control has been
|
||||
/// marked as such. This can be useful when a device state layout has only been partially implemented
|
||||
/// i.e. some data in the state memory has not been mapped to a control, and the unimplemented controls
|
||||
/// are noisy. Without doing this, the device will constantly be made current as the system has no way
|
||||
/// to know that the event data contains only noise.
|
||||
/// </remarks>
|
||||
public bool isNoisy { get; set; }
|
||||
|
||||
internal bool? canRunInBackgroundInternal;
|
||||
|
||||
public bool canRunInBackground
|
||||
{
|
||||
get => canRunInBackgroundInternal.Value;
|
||||
set => canRunInBackgroundInternal = value;
|
||||
}
|
||||
|
||||
internal bool? updateBeforeRenderInternal;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the device should receive events in <see cref="LowLevel.InputUpdateType.BeforeRender"/> updates.
|
||||
/// </summary>
|
||||
/// <seealso cref="InputDevice.updateBeforeRender"/>
|
||||
public bool updateBeforeRender
|
||||
{
|
||||
get => updateBeforeRenderInternal.Value;
|
||||
set => updateBeforeRenderInternal = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true, the layout describes a generic class of devices such as "gamepads" or "mice".
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property also determines how the layout is presented in the UI. All the device layouts
|
||||
/// that are marked as generic kinds of devices are displayed with their own entry at the root level of
|
||||
/// the control picker (<see cref="UnityEngine.InputSystem.Editor.InputControlPicker"/>), for example.
|
||||
/// </remarks>
|
||||
public bool isGenericTypeOfDevice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gives a name to display in the UI. By default, the name is the same as the class the attribute
|
||||
/// is applied to.
|
||||
/// </summary>
|
||||
public string displayName { get; set; }
|
||||
|
||||
public string description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, don't include the layout when presenting picking options in the UI.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will keep device layouts out of the control picker and will keep control layouts out of
|
||||
/// action type dropdowns.
|
||||
/// </remarks>
|
||||
public bool hideInUI { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5256e0d1ebecc84d98a4001ab63d5b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum used to identity the change type for the <see cref="InputSystem.onLayoutChange"/> event.
|
||||
/// </summary>
|
||||
public enum InputControlLayoutChange
|
||||
{
|
||||
Added,
|
||||
Removed,
|
||||
Replaced
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5899dcacd4e3004eaa9107c97e75e09
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,614 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: add a device setup version to InputManager and add version check here to ensure we're not going out of sync
|
||||
|
||||
////REVIEW: can we have a read-only version of this
|
||||
|
||||
////REVIEW: move this to .LowLevel? this one is pretty peculiar to use and doesn't really work like what you'd expect given C#'s List<>
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Keep a list of <see cref="InputControl"/>s without allocating managed memory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is mainly used by methods such as <see cref="InputSystem.FindControls(string)"/>
|
||||
/// or <see cref="InputControlPath.TryFindControls{TControl}"/> to store an arbitrary length
|
||||
/// list of resulting matches without having to allocate GC heap memory.
|
||||
///
|
||||
/// Requires the control setup in the system to not change while the list is being used. If devices are
|
||||
/// removed from the system, the list will no longer be valid. Also, only works with controls of devices that
|
||||
/// have been added to the system (<see cref="InputDevice.added"/>). The reason for these constraints is
|
||||
/// that internally, the list only stores integer indices that are translates to <see cref="InputControl"/>
|
||||
/// references on the fly. If the device setup in the system changes, the indices may become invalid.
|
||||
///
|
||||
/// This struct allocates unmanaged memory and thus must be disposed or it will leak memory. By default
|
||||
/// allocates <c>Allocator.Persistent</c> memory. You can direct it to use another allocator by
|
||||
/// passing an <see cref="Allocator"/> value to one of the constructors.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Find all controls with the "Submit" usage in the system.
|
||||
/// // By wrapping it in a `using` block, the list of controls will automatically be disposed at the end.
|
||||
/// using (var controls = InputSystem.FindControls("*/{Submit}"))
|
||||
/// /* ... */;
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <typeparam name="TControl">Type of <see cref="InputControl"/> to store in the list.</typeparam>
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
[DebuggerTypeProxy(typeof(InputControlListDebugView<>))]
|
||||
#endif
|
||||
public unsafe struct InputControlList<TControl> : IList<TControl>, IReadOnlyList<TControl>, IDisposable
|
||||
where TControl : InputControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Current number of controls in the list.
|
||||
/// </summary>
|
||||
/// <value>Number of controls currently in the list.</value>
|
||||
public int Count => m_Count;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of controls that can currently be stored in the list.
|
||||
/// </summary>
|
||||
/// <value>Total size of array as currently allocated.</value>
|
||||
/// <remarks>
|
||||
/// This can be set ahead of time to avoid repeated allocations.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Add all keys from the keyboard to a list.
|
||||
/// var keys = Keyboard.current.allKeys;
|
||||
/// var list = new InputControlList<KeyControl>(keys.Count);
|
||||
/// list.AddRange(keys);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public int Capacity
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_Indices.IsCreated)
|
||||
return 0;
|
||||
return m_Indices.Length;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
throw new ArgumentException("Capacity cannot be negative", nameof(value));
|
||||
|
||||
if (value == 0)
|
||||
{
|
||||
if (m_Count != 0)
|
||||
m_Indices.Dispose();
|
||||
m_Count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var newSize = value;
|
||||
var allocator = m_Allocator != Allocator.Invalid ? m_Allocator : Allocator.Persistent;
|
||||
ArrayHelpers.Resize(ref m_Indices, newSize, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is always false.
|
||||
/// </summary>
|
||||
/// <value>Always false.</value>
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
/// <summary>
|
||||
/// Return the control at the given index.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of control.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0 or greater than or equal to <see cref="Count"/>
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Internally, the list only stores indices. Resolution to <see cref="InputControl">controls</see> happens
|
||||
/// dynamically by looking them up globally.
|
||||
/// </remarks>
|
||||
public TControl this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= m_Count)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(index), $"Index {index} is out of range in list with {m_Count} entries");
|
||||
|
||||
return FromIndex(m_Indices[index]);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (index < 0 || index >= m_Count)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(index), $"Index {index} is out of range in list with {m_Count} entries");
|
||||
|
||||
m_Indices[index] = ToIndex(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a list that allocates unmanaged memory from the given allocator.
|
||||
/// </summary>
|
||||
/// <param name="allocator">Allocator to use for requesting unmanaged memory.</param>
|
||||
/// <param name="initialCapacity">If greater than zero, will immediately allocate
|
||||
/// memory and set <see cref="Capacity"/> accordingly.</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Create a control list that allocates from the temporary memory allocator.
|
||||
/// using (var list = new InputControlList(Allocator.Temp))
|
||||
/// {
|
||||
/// // Add all gamepads to the list.
|
||||
/// InputSystem.FindControls("<Gamepad>", ref list);
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public InputControlList(Allocator allocator, int initialCapacity = 0)
|
||||
{
|
||||
m_Allocator = allocator;
|
||||
m_Indices = new NativeArray<ulong>();
|
||||
m_Count = 0;
|
||||
|
||||
if (initialCapacity != 0)
|
||||
Capacity = initialCapacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a list and populate it with the given values.
|
||||
/// </summary>
|
||||
/// <param name="values">Sequence of values to populate the list with.</param>
|
||||
/// <param name="allocator">Allocator to use for requesting unmanaged memory.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="values"/> is <c>null</c>.</exception>
|
||||
public InputControlList(IEnumerable<TControl> values, Allocator allocator = Allocator.Persistent)
|
||||
: this(allocator)
|
||||
{
|
||||
if (values == null)
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
|
||||
foreach (var value in values)
|
||||
Add(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a list and add the given values to it.
|
||||
/// </summary>
|
||||
/// <param name="values">Sequence of controls to add to the list.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="values"/> is null.</exception>
|
||||
public InputControlList(params TControl[] values)
|
||||
: this()
|
||||
{
|
||||
if (values == null)
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
|
||||
var count = values.Length;
|
||||
Capacity = Mathf.Max(count, 10);
|
||||
for (var i = 0; i < count; ++i)
|
||||
Add(values[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the list to be exactly <paramref name="size"/> entries. If this is less than the
|
||||
/// current <see cref="Count"/>, additional entries are dropped. If it is more than the
|
||||
/// current <see cref="Count"/>, additional <c>null</c> entries are appended to the list.
|
||||
/// </summary>
|
||||
/// <param name="size">The new value for <see cref="Count"/>.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="size"/> is negative.</exception>
|
||||
/// <remarks>
|
||||
/// <see cref="Capacity"/> is increased if necessary. It will, however, not be decreased if it
|
||||
/// is larger than <paramref name="size"/> entries.
|
||||
/// </remarks>
|
||||
public void Resize(int size)
|
||||
{
|
||||
if (size < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(size), "Size cannot be negative");
|
||||
|
||||
if (Capacity < size)
|
||||
Capacity = size;
|
||||
|
||||
// Initialize newly added entries (if any) such that they produce NULL entries.
|
||||
if (size > Count)
|
||||
UnsafeUtility.MemSet((byte*)m_Indices.GetUnsafePtr() + Count * sizeof(ulong), Byte.MaxValue, size - Count);
|
||||
|
||||
m_Count = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a control to the list.
|
||||
/// </summary>
|
||||
/// <param name="item">Control to add. Allowed to be <c>null</c>.</param>
|
||||
/// <remarks>
|
||||
/// If necessary, <see cref="Capacity"/> will be increased.
|
||||
///
|
||||
/// It is allowed to add nulls to the list. This can be useful, for example, when
|
||||
/// specific indices in the list correlate with specific matches and a given match
|
||||
/// needs to be marked as "matches nothing".
|
||||
/// </remarks>
|
||||
/// <seealso cref="Remove"/>
|
||||
public void Add(TControl item)
|
||||
{
|
||||
var index = ToIndex(item);
|
||||
var allocator = m_Allocator != Allocator.Invalid ? m_Allocator : Allocator.Persistent;
|
||||
ArrayHelpers.AppendWithCapacity(ref m_Indices, ref m_Count, index, allocator: allocator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a slice of elements taken from the given list.
|
||||
/// </summary>
|
||||
/// <param name="list">List to take the slice of values from.</param>
|
||||
/// <param name="count">Number of elements to copy from <paramref name="list"/>.</param>
|
||||
/// <param name="destinationIndex">Starting index in the current control list to copy to.
|
||||
/// This can be beyond <see cref="Count"/> or even <see cref="Capacity"/>. Memory is allocated
|
||||
/// as needed.</param>
|
||||
/// <param name="sourceIndex">Source index in <paramref name="list"/> to start copying from.
|
||||
/// <paramref name="count"/> elements are copied starting at <paramref name="sourceIndex"/>.</param>
|
||||
/// <typeparam name="TList">Type of list. This is a type parameter to avoid boxing in case the
|
||||
/// given list is a struct (such as InputControlList itself).</typeparam>
|
||||
/// <exception cref="ArgumentOutOfRangeException">The range of <paramref name="count"/>
|
||||
/// and <paramref name="sourceIndex"/> is at least partially outside the range of values
|
||||
/// available in <paramref name="list"/>.</exception>
|
||||
public void AddSlice<TList>(TList list, int count = -1, int destinationIndex = -1, int sourceIndex = 0)
|
||||
where TList : IReadOnlyList<TControl>
|
||||
{
|
||||
if (count < 0)
|
||||
count = list.Count;
|
||||
if (destinationIndex < 0)
|
||||
destinationIndex = Count;
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
if (sourceIndex + count > list.Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(count),
|
||||
$"Count of {count} elements starting at index {sourceIndex} exceeds length of list of {list.Count}");
|
||||
|
||||
// Make space in the list.
|
||||
if (Capacity < m_Count + count)
|
||||
Capacity = Math.Max(m_Count + count, 10);
|
||||
if (destinationIndex < Count)
|
||||
NativeArray<ulong>.Copy(m_Indices, destinationIndex, m_Indices, destinationIndex + count,
|
||||
Count - destinationIndex);
|
||||
|
||||
// Add elements.
|
||||
for (var i = 0; i < count; ++i)
|
||||
m_Indices[destinationIndex + i] = ToIndex(list[sourceIndex + i]);
|
||||
m_Count += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a sequence of controls to the list.
|
||||
/// </summary>
|
||||
/// <param name="list">Sequence of controls to add.</param>
|
||||
/// <param name="count">Number of controls from <paramref name="list"/> to add. If negative
|
||||
/// (default), all controls from <paramref name="list"/> will be added.</param>
|
||||
/// <param name="destinationIndex">Index in the control list to start inserting controls
|
||||
/// at. If negative (default), controls will be appended to the end of the control list.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="list"/> is <c>null</c>.</exception>
|
||||
/// <remarks>
|
||||
/// If <paramref name="count"/> is not supplied, <paramref name="list"/> will be iterated
|
||||
/// over twice.
|
||||
/// </remarks>
|
||||
public void AddRange(IEnumerable<TControl> list, int count = -1, int destinationIndex = -1)
|
||||
{
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
|
||||
if (count < 0)
|
||||
count = list.Count();
|
||||
if (destinationIndex < 0)
|
||||
destinationIndex = Count;
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
// Make space in the list.
|
||||
if (Capacity < m_Count + count)
|
||||
Capacity = Math.Max(m_Count + count, 10);
|
||||
if (destinationIndex < Count)
|
||||
NativeArray<ulong>.Copy(m_Indices, destinationIndex, m_Indices, destinationIndex + count,
|
||||
Count - destinationIndex);
|
||||
|
||||
// Add elements.
|
||||
foreach (var element in list)
|
||||
{
|
||||
m_Indices[destinationIndex++] = ToIndex(element);
|
||||
++m_Count;
|
||||
--count;
|
||||
if (count == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a control from the list.
|
||||
/// </summary>
|
||||
/// <param name="item">Control to remove. Can be null.</param>
|
||||
/// <returns>True if the control was found in the list and removed, false otherwise.</returns>
|
||||
/// <seealso cref="Add"/>
|
||||
public bool Remove(TControl item)
|
||||
{
|
||||
if (m_Count == 0)
|
||||
return false;
|
||||
|
||||
var index = ToIndex(item);
|
||||
for (var i = 0; i < m_Count; ++i)
|
||||
{
|
||||
if (m_Indices[i] == index)
|
||||
{
|
||||
ArrayHelpers.EraseAtWithCapacity(m_Indices, ref m_Count, i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the control at the given index.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of control to remove.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is negative or equal
|
||||
/// or greater than <see cref="Count"/>.</exception>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_Count)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(index), $"Index {index} is out of range in list with {m_Count} elements");
|
||||
|
||||
ArrayHelpers.EraseAtWithCapacity(m_Indices, ref m_Count, index);
|
||||
}
|
||||
|
||||
public void CopyTo(TControl[] array, int arrayIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int IndexOf(TControl item)
|
||||
{
|
||||
return IndexOf(item, 0);
|
||||
}
|
||||
|
||||
public int IndexOf(TControl item, int startIndex, int count = -1)
|
||||
{
|
||||
if (startIndex < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex), "startIndex cannot be negative");
|
||||
|
||||
if (m_Count == 0)
|
||||
return -1;
|
||||
|
||||
if (count < 0)
|
||||
count = Mathf.Max(m_Count - startIndex, 0);
|
||||
|
||||
if (startIndex + count > m_Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
var index = ToIndex(item);
|
||||
var indices = (ulong*)m_Indices.GetUnsafeReadOnlyPtr();
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
if (indices[startIndex + i] == index)
|
||||
return startIndex + i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Insert(int index, TControl item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_Count = 0;
|
||||
}
|
||||
|
||||
public bool Contains(TControl item)
|
||||
{
|
||||
return IndexOf(item) != -1;
|
||||
}
|
||||
|
||||
public bool Contains(TControl item, int startIndex, int count = -1)
|
||||
{
|
||||
return IndexOf(item, startIndex, count) != -1;
|
||||
}
|
||||
|
||||
public void SwapElements(int index1, int index2)
|
||||
{
|
||||
if (index1 < 0 || index1 >= m_Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(index1));
|
||||
if (index2 < 0 || index2 >= m_Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(index2));
|
||||
|
||||
if (index1 != index2)
|
||||
m_Indices.SwapElements(index1, index2);
|
||||
}
|
||||
|
||||
public void Sort<TCompare>(int startIndex, int count, TCompare comparer)
|
||||
where TCompare : IComparer<TControl>
|
||||
{
|
||||
if (startIndex < 0 || startIndex >= Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex));
|
||||
if (startIndex + count >= Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
// Simple insertion sort.
|
||||
for (var i = 1; i < count; ++i)
|
||||
for (var j = i; j > 0 && comparer.Compare(this[j - 1], this[j]) < 0; --j)
|
||||
SwapElements(j, j - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the contents of the list to an array.
|
||||
/// </summary>
|
||||
/// <param name="dispose">If true, the control list will be disposed of as part of the operation, i.e.
|
||||
/// <see cref="Dispose"/> will be called as a side-effect.</param>
|
||||
/// <returns>An array mirroring the contents of the list. Not null.</returns>
|
||||
public TControl[] ToArray(bool dispose = false)
|
||||
{
|
||||
// Somewhat pointless to allocate an empty array if we have no elements instead
|
||||
// of returning null, but other ToArray() implementations work that way so we do
|
||||
// the same to avoid surprises.
|
||||
|
||||
var result = new TControl[m_Count];
|
||||
for (var i = 0; i < m_Count; ++i)
|
||||
result[i] = this[i];
|
||||
|
||||
if (dispose)
|
||||
Dispose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void AppendTo(ref TControl[] array, ref int count)
|
||||
{
|
||||
for (var i = 0; i < m_Count; ++i)
|
||||
ArrayHelpers.AppendWithCapacity(ref array, ref count, this[i]);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_Indices.IsCreated)
|
||||
m_Indices.Dispose();
|
||||
}
|
||||
|
||||
public IEnumerator<TControl> GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Count == 0)
|
||||
return "()";
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.Append('(');
|
||||
|
||||
for (var i = 0; i < Count; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
builder.Append(',');
|
||||
builder.Append(this[i]);
|
||||
}
|
||||
|
||||
builder.Append(')');
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private int m_Count;
|
||||
private NativeArray<ulong> m_Indices;
|
||||
private readonly Allocator m_Allocator;
|
||||
|
||||
private const ulong kInvalidIndex = 0xffffffffffffffff;
|
||||
|
||||
private static ulong ToIndex(TControl control)
|
||||
{
|
||||
if (control == null)
|
||||
return kInvalidIndex;
|
||||
|
||||
var device = control.device;
|
||||
var deviceId = device.m_DeviceId;
|
||||
var controlIndex = !ReferenceEquals(device, control)
|
||||
? device.m_ChildrenForEachControl.IndexOfReference<InputControl, InputControl>(control) + 1
|
||||
: 0;
|
||||
|
||||
// There is a known documented bug with the new Rosyln
|
||||
// compiler where it warns on casts with following line that
|
||||
// was perfectly legal in previous CSC compiler.
|
||||
// Below is silly conversion to get rid of warning, or we can pragma
|
||||
// out the warning.
|
||||
//return ((ulong)deviceId << 32) | (ulong)controlIndex;
|
||||
var shiftedDeviceId = (ulong)deviceId << 32;
|
||||
var unsignedControlIndex = (ulong)controlIndex;
|
||||
|
||||
return shiftedDeviceId | unsignedControlIndex;
|
||||
}
|
||||
|
||||
private static TControl FromIndex(ulong index)
|
||||
{
|
||||
if (index == kInvalidIndex)
|
||||
return null;
|
||||
|
||||
var deviceId = (int)(index >> 32);
|
||||
var controlIndex = (int)(index & 0xFFFFFFFF);
|
||||
|
||||
var device = InputSystem.GetDeviceById(deviceId);
|
||||
if (device == null)
|
||||
return null;
|
||||
if (controlIndex == 0)
|
||||
return (TControl)(InputControl)device;
|
||||
|
||||
return (TControl)device.m_ChildrenForEachControl[controlIndex - 1];
|
||||
}
|
||||
|
||||
private struct Enumerator : IEnumerator<TControl>
|
||||
{
|
||||
private readonly ulong* m_Indices;
|
||||
private readonly int m_Count;
|
||||
private int m_Current;
|
||||
|
||||
public Enumerator(InputControlList<TControl> list)
|
||||
{
|
||||
m_Count = list.m_Count;
|
||||
m_Current = -1;
|
||||
m_Indices = m_Count > 0 ? (ulong*)list.m_Indices.GetUnsafeReadOnlyPtr() : null;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (m_Current >= m_Count)
|
||||
return false;
|
||||
++m_Current;
|
||||
return (m_Current != m_Count);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
m_Current = -1;
|
||||
}
|
||||
|
||||
public TControl Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Indices == null)
|
||||
throw new InvalidOperationException("Enumerator is not valid");
|
||||
return FromIndex(m_Indices[m_Current]);
|
||||
}
|
||||
}
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
internal struct InputControlListDebugView<TControl>
|
||||
where TControl : InputControl
|
||||
{
|
||||
private readonly TControl[] m_Controls;
|
||||
|
||||
public InputControlListDebugView(InputControlList<TControl> list)
|
||||
{
|
||||
m_Controls = list.ToArray();
|
||||
}
|
||||
|
||||
public TControl[] controls => m_Controls;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 543792e79adf14478b4a00527bbefce1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce150b9c1acd40e0a087ca3f3257831d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
////TODO: come up with a mechanism to allow (certain) processors to be stateful
|
||||
|
||||
////TODO: cache processors globally; there's no need to instantiate the same processor with the same parameters multiple times
|
||||
//// (except if they do end up being stateful)
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// A processor that conditions/transforms input values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To define a custom processor, it is usable best to derive from <see cref="InputProcessor{TValue}"/>
|
||||
/// instead of from this class. Doing so will avoid having to deal with things such as the raw memory
|
||||
/// buffers of <see cref="Process"/>.
|
||||
///
|
||||
/// Note, however, that if you do want to define a processor that can process more than one type of
|
||||
/// value, you can derive directly from this class.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputBinding.processors"/>
|
||||
/// <seealso cref="InputControlLayout.ControlItem.processors"/>
|
||||
/// <seealso cref="InputSystem.RegisterProcessor{T}"/>
|
||||
/// <seealso cref="InputActionRebindingExtensions.GetParameterValue(InputAction,string,InputBinding)"/>
|
||||
/// <seealso cref="InputActionRebindingExtensions.ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/>
|
||||
public abstract class InputProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Process an input value, given as an object, and return the processed value as an object.
|
||||
/// </summary>
|
||||
/// <param name="value">A value matching the processor's value type.</param>
|
||||
/// <param name="control">Optional control that the value originated from. Must have the same value type
|
||||
/// that the processor has.</param>
|
||||
/// <returns>A processed value based on <paramref name="value"/>.</returns>
|
||||
/// <remarks>
|
||||
/// This method allocates GC heap memory. To process values without allocating GC memory, it is necessary to either know
|
||||
/// the value type of a processor at compile time and call <see cref="InputProcessor{TValue}.Process(TValue,UnityEngine.InputSystem.InputControl)"/>
|
||||
/// directly or to use <see cref="Process"/> instead and process values in raw memory buffers.
|
||||
/// </remarks>
|
||||
public abstract object ProcessAsObject(object value, InputControl control);
|
||||
|
||||
/// <summary>
|
||||
/// Process an input value stored in the given memory buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Memory buffer containing the input value. Must be at least large enough
|
||||
/// to hold one full value as indicated by <paramref name="bufferSize"/>.</param>
|
||||
/// <param name="bufferSize">Size (in bytes) of the value inside <paramref name="buffer"/>.</param>
|
||||
/// <param name="control">Optional control that the value originated from. Must have the same value type
|
||||
/// that the processor has.</param>
|
||||
/// <remarks>
|
||||
/// This method allows processing values of arbitrary size without allocating memory on the GC heap.
|
||||
/// </remarks>
|
||||
public abstract unsafe void Process(void* buffer, int bufferSize, InputControl control);
|
||||
|
||||
internal static TypeTable s_Processors;
|
||||
|
||||
/// <summary>
|
||||
/// Get the value type of a processor without having to instantiate it and use <see cref="valueType"/>.
|
||||
/// </summary>
|
||||
/// <param name="processorType"></param>
|
||||
/// <returns>Value type of the given processor or null if it could not be determined statically.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="processorType"/> is null.</exception>
|
||||
/// <remarks>
|
||||
/// This method is reliant on the processor being based on <see cref="InputProcessor{TValue}"/>. It will return
|
||||
/// the <c>TValue</c> argument used with the class. If the processor is not based on <see cref="InputProcessor{TValue}"/>,
|
||||
/// this method returns null.
|
||||
/// </remarks>
|
||||
internal static Type GetValueTypeFromType(Type processorType)
|
||||
{
|
||||
if (processorType == null)
|
||||
throw new ArgumentNullException(nameof(processorType));
|
||||
|
||||
return TypeHelpers.GetGenericTypeArgumentFromHierarchy(processorType, typeof(InputProcessor<>), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Caching policy regarding usage of return value from processors.
|
||||
/// </summary>
|
||||
public enum CachingPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// Cache result value if unprocessed value has not been changed.
|
||||
/// </summary>
|
||||
CacheResult = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Process value every call to <see cref="InputControl{TValue}.ReadValue()"/> even if unprocessed value has not been changed.
|
||||
/// </summary>
|
||||
EvaluateOnEveryRead = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Caching policy of the processor. Override this property to provide a different value.
|
||||
/// </summary>
|
||||
public virtual CachingPolicy cachingPolicy => CachingPolicy.CacheResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A processor that conditions/transforms input values.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">Type of value to be processed. Only InputControls that use the
|
||||
/// same value type will be compatible with the processor.</typeparam>
|
||||
/// <remarks>
|
||||
/// Each <see cref="InputControl"/> can have a stack of processors assigned to it.
|
||||
///
|
||||
/// Note that processors CANNOT be stateful. If you need processing that requires keeping
|
||||
/// mutating state over time, use InputActions. All mutable state needs to be
|
||||
/// kept in the central state buffers.
|
||||
///
|
||||
/// However, processors can have configurable parameters. Every public field on a processor
|
||||
/// object can be set using "parameters" in JSON or by supplying parameters through the
|
||||
/// <see cref="InputControlAttribute.processors"/> field.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public class ScalingProcessor : InputProcessor<float>
|
||||
/// {
|
||||
/// // This field can be set as a parameter. See examples below.
|
||||
/// // If not explicitly configured, will have its default value.
|
||||
/// public float factor = 2.0f;
|
||||
///
|
||||
/// public float Process(float value, InputControl control)
|
||||
/// {
|
||||
/// return value * factor;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Use processor in JSON:
|
||||
/// const string json = @"
|
||||
/// {
|
||||
/// ""name"" : ""MyDevice"",
|
||||
/// ""controls"" : [
|
||||
/// { ""name"" : ""axis"", ""layout"" : ""Axis"", ""processors"" : ""scale(factor=4)"" }
|
||||
/// ]
|
||||
/// }
|
||||
/// ";
|
||||
///
|
||||
/// // Use processor on C# state struct:
|
||||
/// public struct MyDeviceState : IInputStateTypeInfo
|
||||
/// {
|
||||
/// [InputControl(layout = "Axis", processors = "scale(factor=4)"]
|
||||
/// public float axis;
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// See <see cref="Editor.InputParameterEditor{T}"/> for how to define custom parameter
|
||||
/// editing UIs for processors.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputSystem.RegisterProcessor"/>
|
||||
public abstract class InputProcessor<TValue> : InputProcessor
|
||||
where TValue : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Process the given value and return the result.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The implementation of this method must not be stateful.
|
||||
/// </remarks>
|
||||
/// <param name="value">Input value to process.</param>
|
||||
/// <param name="control">Control that the value originally came from. This can be null if the value did
|
||||
/// not originate from a control. This can be the case, for example, if the processor sits on a composite
|
||||
/// binding (<see cref="InputBindingComposite"/>) as composites are not directly associated with controls
|
||||
/// but rather source their values through their child bindings.</param>
|
||||
/// <returns>Processed input value.</returns>
|
||||
public abstract TValue Process(TValue value, InputControl control);
|
||||
|
||||
public override object ProcessAsObject(object value, InputControl control)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
|
||||
if (!(value is TValue))
|
||||
throw new ArgumentException(
|
||||
$"Expecting value of type '{typeof(TValue).Name}' but got value '{value}' of type '{value.GetType().Name}'",
|
||||
nameof(value));
|
||||
|
||||
var valueOfType = (TValue)value;
|
||||
|
||||
return Process(valueOfType, control);
|
||||
}
|
||||
|
||||
public override unsafe void Process(void* buffer, int bufferSize, InputControl control)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
var valueSize = UnsafeUtility.SizeOf<TValue>();
|
||||
if (bufferSize < valueSize)
|
||||
throw new ArgumentException(
|
||||
$"Expected buffer of at least {valueSize} bytes but got buffer with just {bufferSize} bytes",
|
||||
nameof(bufferSize));
|
||||
|
||||
var value = default(TValue);
|
||||
var valuePtr = UnsafeUtility.AddressOf(ref value);
|
||||
UnsafeUtility.MemCpy(valuePtr, buffer, valueSize);
|
||||
|
||||
value = Process(value, control);
|
||||
|
||||
valuePtr = UnsafeUtility.AddressOf(ref value);
|
||||
UnsafeUtility.MemCpy(buffer, valuePtr, valueSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c79fd316c31a2402692c908865404608
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: this or the layout system needs to detect when the format isn't supported by the control
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A generic input control reading integer values.
|
||||
/// </summary>
|
||||
public class IntegerControl : InputControl<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Default-initialize an integer control.
|
||||
/// </summary>
|
||||
public IntegerControl()
|
||||
{
|
||||
m_StateBlock.format = InputStateBlock.FormatInt;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override unsafe int ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
switch (m_OptimizedControlDataType)
|
||||
{
|
||||
case InputStateBlock.kFormatInt:
|
||||
return *(int*)((byte*)statePtr + (int)m_StateBlock.byteOffset);
|
||||
default:
|
||||
return m_StateBlock.ReadInt(statePtr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override unsafe void WriteValueIntoState(int value, void* statePtr)
|
||||
{
|
||||
switch (m_OptimizedControlDataType)
|
||||
{
|
||||
case InputStateBlock.kFormatInt:
|
||||
*(int*)((byte*)statePtr + (int)m_StateBlock.byteOffset) = value;
|
||||
break;
|
||||
default:
|
||||
m_StateBlock.WriteInt(statePtr, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override FourCC CalculateOptimizedControlDataType()
|
||||
{
|
||||
if (m_StateBlock.format == InputStateBlock.FormatInt &&
|
||||
m_StateBlock.sizeInBits == 32 &&
|
||||
m_StateBlock.bitOffset == 0)
|
||||
return InputStateBlock.FormatInt;
|
||||
return InputStateBlock.FormatInvalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7009ea2cce278c14d852537c8f40c8a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,100 @@
|
||||
using System.Globalization;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A key on a <see cref="Keyboard"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is an extended button control which adds various features to account for the fact that keys
|
||||
/// have symbols associated with them which may change depending on keyboard layout as well as in combination
|
||||
/// with other keys.
|
||||
///
|
||||
/// > [!NOTE]
|
||||
/// > Unity input system key codes and input manager key codes are designed with game controls in mind.
|
||||
///
|
||||
/// This means the way they are assigned is intended to preserve the location of keys on keyboards,
|
||||
/// so that pressing a key in the same location on different keyboards should result in the same action
|
||||
/// regardless of what is printed on a key or what current system language is set.
|
||||
///
|
||||
/// This means, for example, that <see cref="Key.A"/> is always the key to the right of <see cref="Key.CapsLock"/>,
|
||||
/// regardless of which key (if any) produces the "a" character on the current keyboard layout.
|
||||
///
|
||||
/// Unity relies on physical hardware in the keyboards to report same USB HID "usage" for the keys in
|
||||
/// the same location.This puts a practical limit on what can be achieved, because different keyboards
|
||||
/// might report different data, and this is outside of Unity's control.
|
||||
///
|
||||
/// For this reason, you should not use key codes to read text input.
|
||||
/// Instead, you should use the <see cref="Keyboard.onTextInput"/> callback.
|
||||
/// The `onTextInput` callback provides you with the actual text characters which correspond
|
||||
/// to the symbols printed on a keyboard, based on the end user's current system language layout.
|
||||
///
|
||||
/// To find the text character (if any) generated by a key according to the currently active keyboard
|
||||
/// layout, use the <see cref="InputControl.displayName"/> property of <see cref="KeyControl"/>.
|
||||
/// </remarks>
|
||||
public class KeyControl : ButtonControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The code used in Unity to identify the key.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property must be initialized by <see cref="InputControl.FinishSetup"/> of
|
||||
/// the device owning the control.
|
||||
/// You should not use `keyCode` to read text input. For more information, <see cref="KeyControl"/>
|
||||
/// </remarks>
|
||||
public Key keyCode { get; set; }
|
||||
|
||||
////REVIEW: rename this to something like platformKeyCode? We're not really dealing with scan code here.
|
||||
/// <summary>
|
||||
/// The code that the underlying platform uses to identify the key.
|
||||
/// </summary>
|
||||
public int scanCode
|
||||
{
|
||||
get
|
||||
{
|
||||
RefreshConfigurationIfNeeded();
|
||||
return m_ScanCode;
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override void RefreshConfiguration()
|
||||
{
|
||||
// Wipe our last cached set of data (if any).
|
||||
displayName = null;
|
||||
m_ScanCode = 0;
|
||||
|
||||
var command = QueryKeyNameCommand.Create(keyCode);
|
||||
if (device.ExecuteCommand(ref command) > 0)
|
||||
{
|
||||
m_ScanCode = command.scanOrKeyCode;
|
||||
|
||||
var rawKeyName = command.ReadKeyName();
|
||||
if (string.IsNullOrEmpty(rawKeyName))
|
||||
{
|
||||
displayName = rawKeyName;
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to lower case first because ToTitleCase preserves upper casing.
|
||||
// For example on Swedish Windows layout right shift display name is "HÖGER SKIFT".
|
||||
// Just passing it to ToTitleCase won't change anything. But passing "höger skift" will return "Höger Skift".
|
||||
var keyNameLowerCase = rawKeyName.ToLowerInvariant();
|
||||
|
||||
if (string.IsNullOrEmpty(keyNameLowerCase))
|
||||
{
|
||||
displayName = rawKeyName;
|
||||
return;
|
||||
}
|
||||
|
||||
var textInfo = CultureInfo.InvariantCulture.TextInfo;
|
||||
displayName = textInfo.ToTitleCase(keyNameLowerCase);
|
||||
}
|
||||
}
|
||||
|
||||
// Cached configuration data for the key. We fetch this from the
|
||||
// device on demand.
|
||||
private int m_ScanCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d7a42e3bb8b4c0f877cbe19718a4103
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fde91b0a4ee45cd86be18a8d829dc87
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine.InputSystem.Editor;
|
||||
using UnityEngine.UIElements;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Clamps values to the range given by <see cref="min"/> and <see cref="max"/> and re-normalizes the resulting
|
||||
/// value to [0..1].
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This processor is registered (see <see cref="InputSystem.RegisterProcessor{T}"/>) under the name "AxisDeadzone".
|
||||
///
|
||||
/// It acts like a combination of <see cref="ClampProcessor"/> and <see cref="NormalizeProcessor"/>.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// </code>
|
||||
/// // Bind to right trigger on gamepad such that the value is clamped and normalized between
|
||||
/// // 0.3 and 0.7.
|
||||
/// new InputAction(binding: "<Gamepad>/rightTrigger", processors: "axisDeadzone(min=0.3,max=0.7)");
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="StickDeadzoneProcessor"/>
|
||||
public class AxisDeadzoneProcessor : InputProcessor<float>
|
||||
{
|
||||
/// <summary>
|
||||
/// Lower bound (inclusive) below which input values get clamped. Corresponds to 0 in the normalized range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this is equal to 0 (the default), <see cref="InputSettings.defaultDeadzoneMin"/> is used instead.
|
||||
/// </remarks>
|
||||
public float min;
|
||||
|
||||
/// <summary>
|
||||
/// Upper bound (inclusive) beyond which input values get clamped. Corresponds to 1 in the normalized range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this is equal to 0 (the default), <see cref="InputSettings.defaultDeadzoneMax"/> is used instead.
|
||||
/// </remarks>
|
||||
public float max;
|
||||
|
||||
private float minOrDefault => min == default ? InputSystem.settings.defaultDeadzoneMin : min;
|
||||
private float maxOrDefault => max == default ? InputSystem.settings.defaultDeadzoneMax : max;
|
||||
|
||||
/// <summary>
|
||||
/// Normalize <paramref name="value"/> according to <see cref="min"/> and <see cref="max"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Normalized value.</returns>
|
||||
public override float Process(float value, InputControl control = null)
|
||||
{
|
||||
var min = minOrDefault;
|
||||
var max = maxOrDefault;
|
||||
|
||||
var absValue = Mathf.Abs(value);
|
||||
if (absValue < min)
|
||||
return 0;
|
||||
if (absValue > max)
|
||||
return Mathf.Sign(value);
|
||||
|
||||
return Mathf.Sign(value) * ((absValue - min) / (max - min));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"AxisDeadzone(min={minOrDefault},max={maxOrDefault})";
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal class AxisDeadzoneProcessorEditor : InputParameterEditor<AxisDeadzoneProcessor>
|
||||
{
|
||||
protected override void OnEnable()
|
||||
{
|
||||
m_MinSetting.Initialize("Min",
|
||||
"Value below which input values will be clamped. After clamping, values will be renormalized to [0..1] between min and max.",
|
||||
"Default Deadzone Min",
|
||||
() => target.min, v => target.min = v,
|
||||
() => InputSystem.settings.defaultDeadzoneMin);
|
||||
m_MaxSetting.Initialize("Max",
|
||||
"Value above which input values will be clamped. After clamping, values will be renormalized to [0..1] between min and max.",
|
||||
"Default Deadzone Max",
|
||||
() => target.max, v => target.max = v,
|
||||
() => InputSystem.settings.defaultDeadzoneMax);
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
|
||||
{
|
||||
m_MinSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
m_MaxSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
}
|
||||
|
||||
private CustomOrDefaultSetting m_MinSetting;
|
||||
private CustomOrDefaultSetting m_MaxSetting;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 110e42b335c6744e482ada733cbda674
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
////TODO: move clamping settings into struct and add process function; then embed both here and in AxisControl
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Clamp a floating-point input to between <see cref="min"/> and <see cref="max"/>. This is equivalent
|
||||
/// to <c>Mathf.Clamp(value, min, max)</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This processor is registered (see <see cref="InputSystem.RegisterProcessor{T}"/>) under the name "clamp" by default.
|
||||
///
|
||||
/// Note that no normalization is performed. If you want to re-normalize the input value after clamping,
|
||||
/// add a <see cref="NormalizeProcessor"/>. Alternatively, add a <see cref="AxisDeadzoneProcessor"/> which
|
||||
/// both clamps and normalizes.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// </code>
|
||||
/// // Bind to right trigger on gamepad such that the value never drops below 0.3 and never goes
|
||||
/// // above 0.7.
|
||||
/// new InputAction(binding: "<Gamepad>/rightTrigger", processors: "clamp(min=0.3,max=0.7)");
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public class ClampProcessor : InputProcessor<float>
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimum value (inclusive!) of the accepted value range.
|
||||
/// </summary>
|
||||
public float min;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum value (inclusive!) of the accepted value range.
|
||||
/// </summary>
|
||||
public float max;
|
||||
|
||||
/// <summary>
|
||||
/// Clamp <paramref name="value"/> to the range of <see cref="min"/> and <see cref="max"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Clamped value.</returns>
|
||||
public override float Process(float value, InputControl control)
|
||||
{
|
||||
return Mathf.Clamp(value, min, max);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Clamp(min={min},max={max})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6887ac562f464a329bc468158dbbf18f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
[DesignTimeVisible(false)]
|
||||
internal class CompensateDirectionProcessor : InputProcessor<Vector3>
|
||||
{
|
||||
public override Vector3 Process(Vector3 value, InputControl control)
|
||||
{
|
||||
if (!InputSystem.settings.compensateForScreenOrientation)
|
||||
return value;
|
||||
|
||||
var rotation = Quaternion.identity;
|
||||
switch (InputRuntime.s_Instance.screenOrientation)
|
||||
{
|
||||
case ScreenOrientation.PortraitUpsideDown: rotation = Quaternion.Euler(0, 0, 180); break;
|
||||
case ScreenOrientation.LandscapeLeft: rotation = Quaternion.Euler(0, 0, 90); break;
|
||||
case ScreenOrientation.LandscapeRight: rotation = Quaternion.Euler(0, 0, 270); break;
|
||||
}
|
||||
return rotation * value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "CompensateDirection()";
|
||||
}
|
||||
|
||||
public override CachingPolicy cachingPolicy => CachingPolicy.EvaluateOnEveryRead;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbce6ac3ce95e4e28af8b2212171619a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
[DesignTimeVisible(false)]
|
||||
internal class CompensateRotationProcessor : InputProcessor<Quaternion>
|
||||
{
|
||||
public override Quaternion Process(Quaternion value, InputControl control)
|
||||
{
|
||||
if (!InputSystem.settings.compensateForScreenOrientation)
|
||||
return value;
|
||||
|
||||
const float kSqrtOfTwo = 1.4142135623731f;
|
||||
var q = Quaternion.identity;
|
||||
|
||||
switch (InputRuntime.s_Instance.screenOrientation)
|
||||
{
|
||||
case ScreenOrientation.PortraitUpsideDown: q = new Quaternion(0.0f, 0.0f, 1.0f /*sin(pi/2)*/, 0.0f /*cos(pi/2)*/); break;
|
||||
case ScreenOrientation.LandscapeLeft: q = new Quaternion(0.0f, 0.0f, kSqrtOfTwo * 0.5f /*sin(pi/4)*/, -kSqrtOfTwo * 0.5f /*cos(pi/4)*/); break;
|
||||
case ScreenOrientation.LandscapeRight: q = new Quaternion(0.0f, 0.0f, -kSqrtOfTwo * 0.5f /*sin(3pi/4)*/, -kSqrtOfTwo * 0.5f /*cos(3pi/4)*/); break;
|
||||
}
|
||||
|
||||
return value * q;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "CompensateRotation()";
|
||||
}
|
||||
|
||||
public override CachingPolicy cachingPolicy => CachingPolicy.EvaluateOnEveryRead;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ef7b744d03d44f2a82c38ac2f25b21f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
#if UNITY_EDITOR || PACKAGE_DOCS_GENERATION
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// If Unity is currently in an <see cref="EditorWindow"/> callback, transforms a 2D coordinate from
|
||||
/// player window space into window space of the current EditorWindow.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This processor is only available in the editor. Also, it only works on devices that
|
||||
/// support the <see cref="QueryEditorWindowCoordinatesCommand"/> request.
|
||||
///
|
||||
/// Outside of <see cref="EditorWindow"/> callbacks, this processor does nothing and just passes through
|
||||
/// the coordinates it receives.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Pointer.position"/>
|
||||
[DesignTimeVisible(false)]
|
||||
public class EditorWindowSpaceProcessor : InputProcessor<Vector2>
|
||||
{
|
||||
/// <summary>
|
||||
/// Transform the given player screen-space coordinate into the coordinate space of the current
|
||||
/// <c>EditorWindow</c>.
|
||||
/// </summary>
|
||||
/// <param name="value">GameView screen space coordinate.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>The given coordinate transformed into <c>EditorWindow</c> space.</returns>
|
||||
/// <remarks>
|
||||
/// This method will only succeed if the editor is currently in an <c>EditorWindow</c> callback such
|
||||
/// as <c>OnGUI</c>.
|
||||
/// </remarks>
|
||||
public override Vector2 Process(Vector2 value, InputControl control)
|
||||
{
|
||||
// We go and fire trigger QueryEditorWindowCoordinatesCommand regardless
|
||||
// of whether we are currently in EditorWindow code or not. The expectation
|
||||
// here is that the underlying editor code is in a better position than us
|
||||
// to judge whether the conversion should be performed or not. In native code,
|
||||
// the IOCTL implementations will early out if they detect that the current
|
||||
// EditorWindow is in fact a game view.
|
||||
|
||||
if (Mouse.s_PlatformMouseDevice != null)
|
||||
{
|
||||
var command = QueryEditorWindowCoordinatesCommand.Create(value);
|
||||
// Not all pointer devices implement the editor window position IOCTL,
|
||||
// so we try the global mouse device if available.
|
||||
if (Mouse.s_PlatformMouseDevice.ExecuteCommand(ref command) > 0)
|
||||
return command.inOutCoordinates;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87ce818774f4c544882b249f32619300
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// An input processor that inverts its input value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This process is registered (see <see cref="InputSystem.RegisterProcessor{T}"/> as "invert" by default.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Bind to the gamepad's left trigger such that it returns inverted values.
|
||||
/// new InputAction(binding: "<Gamepad>/leftTrigger", processors="invert");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public class InvertProcessor : InputProcessor<float>
|
||||
{
|
||||
/// <summary>
|
||||
/// Return the inverted value of <paramref name="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Invert value.</returns>
|
||||
public override float Process(float value, InputControl control)
|
||||
{
|
||||
return value * -1.0f;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return "Invert()";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84e410cdded84dd3971c9dbe94605503
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Inverts the <c>x</c> and/or <c>y</c> channel of a <c>Vector2</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This process is registered (see <see cref="InputSystem.RegisterProcessor{T}"/> as "invertVector2" by default.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Bind to the left stick on the gamepad such that its Y channel is inverted.
|
||||
/// new InputAction(binding: "<Gamepad>/leftStick", processors="invertVector2(invertY,invertX=false)");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="InvertVector3Processor"/>
|
||||
public class InvertVector2Processor : InputProcessor<Vector2>
|
||||
{
|
||||
/// <summary>
|
||||
/// If true, the <c>x</c> channel of the <c>Vector2</c> input value is inverted. True by default.
|
||||
/// </summary>
|
||||
public bool invertX = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the <c>y</c> channel of the <c>Vector2</c> input value is inverted. True by default.
|
||||
/// </summary>
|
||||
public bool invertY = true;
|
||||
|
||||
/// <summary>
|
||||
/// Invert the <c>x</c> and/or <c>y</c> channel of the given <paramref name="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Vector2 with inverted channels.</returns>
|
||||
public override Vector2 Process(Vector2 value, InputControl control)
|
||||
{
|
||||
if (invertX)
|
||||
value.x *= -1;
|
||||
if (invertY)
|
||||
value.y *= -1;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"InvertVector2(invertX={invertX},invertY={invertY})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ab0ce1980d384014be67a2d135ed92c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Inverts the <c>x</c> and/or <c>y</c> and/or <c>z</c> channel of a <c>Vector3</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This process is registered (see <see cref="InputSystem.RegisterProcessor{T}"/> as "invertVector3" by default.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Bind to gravity sensor such that its Y value is inverted.
|
||||
/// new InputAction(binding: "<GravitySensor>/gravity", processors="invertVector3(invertX=false,invertY,invertZ=false)");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="InvertVector2Processor"/>
|
||||
public class InvertVector3Processor : InputProcessor<Vector3>
|
||||
{
|
||||
/// <summary>
|
||||
/// If true, the <c>x</c> channel of the <c>Vector3</c> input value is inverted. True by default.
|
||||
/// </summary>
|
||||
public bool invertX = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the <c>y</c> channel of the <c>Vector3</c> input value is inverted. True by default.
|
||||
/// </summary>
|
||||
public bool invertY = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the <c>z</c> channel of the <c>Vector3</c> input value is inverted. True by default.
|
||||
/// </summary>
|
||||
public bool invertZ = true;
|
||||
|
||||
/// <summary>
|
||||
/// Return the given vector with the respective channels being inverted.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Vector with channels inverted according to <see cref="invertX"/>, <see cref="invertY"/>, and <see cref="invertZ"/>.</returns>
|
||||
public override Vector3 Process(Vector3 value, InputControl control)
|
||||
{
|
||||
if (invertX)
|
||||
value.x *= -1;
|
||||
if (invertY)
|
||||
value.y *= -1;
|
||||
if (invertZ)
|
||||
value.z *= -1;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"InvertVector3(invertX={invertX},invertY={invertY},invertZ={invertZ})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7938861606c2845d19cf2e8a683ddba3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
////REVIEW: handle values dropping below min and above max?
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Normalizes input values in the range <see cref="min"/> and <see cref="max"/> to
|
||||
/// unsigned normalized form [0..1] if <see cref="zero"/> is placed at (or below) <see cref="min"/>
|
||||
/// or to signed normalized form [-1..1] if <see cref="zero"/> is placed in-between
|
||||
/// <see cref="min"/> and <see cref="max"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This processor is registered (see <see cref="InputSystem.RegisterProcessor{T}"/>) under the name "normalize".
|
||||
///
|
||||
/// Note that this processor does not clamp the incoming value to <see cref="min"/> and <see cref="max"/>.
|
||||
/// To achieve this, either add a <see cref="ClampProcessor"/> or use <see cref="AxisDeadzoneProcessor"/>
|
||||
/// which combines clamping and normalizing.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// </code>
|
||||
/// // Bind to right trigger on gamepad such that the value values below 0.3 and above 0.7 get normalized
|
||||
/// // to values between [0..1].
|
||||
/// new InputAction(binding: "<Gamepad>/rightTrigger", processors: "normalize(min=0.3,max=0.7)");
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="NormalizeVector2Processor"/>
|
||||
/// <seealso cref="NormalizeVector3Processor"/>
|
||||
public class NormalizeProcessor : InputProcessor<float>
|
||||
{
|
||||
/// <summary>
|
||||
/// Input value (inclusive) that corresponds to 0 or -1 (depending on <see cref="zero"/>), the lower bound.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the input value drops below min, the result is undefined.
|
||||
/// </remarks>
|
||||
public float min;
|
||||
|
||||
/// <summary>
|
||||
/// Input value (inclusive) that corresponds to 1, the upper bound.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the input value goes beyond max, the result is undefined.
|
||||
/// </remarks>
|
||||
public float max;
|
||||
|
||||
/// <summary>
|
||||
/// Input value that corresponds to 0. If this is placed at or below <see cref="min"/>, the resulting normalization
|
||||
/// returns a [0..1] value. If this is placed in-between <see cref="min"/> and <see cref="max"/>, the resulting
|
||||
/// normalization returns a [-1..1] value.
|
||||
/// </summary>
|
||||
public float zero;
|
||||
|
||||
/// <summary>
|
||||
/// Normalize <paramref name="value"/> with respect to <see cref="min"/> and <see cref="max"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Normalized value.</returns>
|
||||
public override float Process(float value, InputControl control)
|
||||
{
|
||||
return Normalize(value, min, max, zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalize <paramref name="value"/> with respect to <paramref name="min"/> and <paramref name="max"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <param name="min">Lower bound. See <see cref="min"/>.</param>
|
||||
/// <param name="max">Upper bound. See <see cref="max"/>.</param>
|
||||
/// <param name="zero">Zero point. See <see cref="zero"/>.</param>
|
||||
/// <returns>Normalized value.</returns>
|
||||
/// <remarks>
|
||||
/// This method performs the same function as <see cref="Process"/>.
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Normalize 2 against a [1..5] range. Returns 0.25.
|
||||
/// NormalizeProcessor.Normalize(2, 1, 5, 1)
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static float Normalize(float value, float min, float max, float zero)
|
||||
{
|
||||
if (zero < min)
|
||||
zero = min;
|
||||
// Prevent NaN/Inf from dividing 0 by something.
|
||||
if (Mathf.Approximately(value, min))
|
||||
{
|
||||
if (min < zero)
|
||||
return -1f;
|
||||
return 0f;
|
||||
}
|
||||
var percentage = (value - min) / (max - min);
|
||||
if (min < zero)
|
||||
return 2 * percentage - 1;
|
||||
return percentage;
|
||||
}
|
||||
|
||||
internal static float Denormalize(float value, float min, float max, float zero)
|
||||
{
|
||||
if (zero < min)
|
||||
zero = min;
|
||||
|
||||
if (min < zero)
|
||||
{
|
||||
if (value < 0)
|
||||
return min + (zero - min) * (value * -1f);
|
||||
return zero + (max - zero) * value;
|
||||
}
|
||||
|
||||
return min + (max - min) * value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Normalize(min={min},max={max},zero={zero})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94995588a5664878be30498a8ace6ba2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Normalizes a <c>Vector2</c> input value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This processor is registered (see <see cref="InputSystem.RegisterProcessor{T}"/>) under the name "normalizeVector2".
|
||||
/// </remarks>
|
||||
/// <seealso cref="NormalizeVector3Processor"/>
|
||||
public class NormalizeVector2Processor : InputProcessor<Vector2>
|
||||
{
|
||||
/// <summary>
|
||||
/// Normalize <paramref name="value"/>. Performs the equivalent of <c>value.normalized</c>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input vector.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Normalized vector.</returns>
|
||||
public override Vector2 Process(Vector2 value, InputControl control)
|
||||
{
|
||||
return value.normalized;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return "NormalizeVector2()";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ae082cf4f6634e9d8cdd8526dd816a3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Normalizes a <c>Vector3</c> input value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This processor is registered (see <see cref="InputSystem.RegisterProcessor{T}"/>) under the name "normalizeVector3".
|
||||
/// </remarks>
|
||||
/// <seealso cref="NormalizeVector2Processor"/>
|
||||
public class NormalizeVector3Processor : InputProcessor<Vector3>
|
||||
{
|
||||
/// <summary>
|
||||
/// Normalize <paramref name="value"/>. Performs the equivalent of <c>value.normalized</c>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input vector.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Normalized vector.</returns>
|
||||
public override Vector3 Process(Vector3 value, InputControl control)
|
||||
{
|
||||
return value.normalized;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return "NormalizeVector3()";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b78cdd0ba4454fd69b2be04e825d921
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scale a float value by a constant factor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This processor is registered (see <see cref="InputSystem.RegisterProcessor{T}"/>) under the name "scale".
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// </code>
|
||||
/// // Bind to left trigger on the gamepad such that its values are scaled by a factor of 2.
|
||||
/// new InputAction(binding: "<Gamepad>/leftTrigger", processors: "scale(factor=2)");
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="ScaleVector2Processor"/>
|
||||
/// <seealso cref="ScaleVector3Processor"/>
|
||||
public class ScaleProcessor : InputProcessor<float>
|
||||
{
|
||||
/// <summary>
|
||||
/// Scale factor to apply to incoming input values. Defaults to 1 (no scaling).
|
||||
/// </summary>
|
||||
[Tooltip("Scale factor to multiply incoming float values by.")]
|
||||
public float factor = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Scale the given <paramref name="value"/> by <see cref="factor"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Scaled value.</returns>
|
||||
public override float Process(float value, InputControl control)
|
||||
{
|
||||
return value * factor;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Scale(factor={factor})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98590516d066443e58df0deff4a95b21
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scale the components of a <see cref="Vector2"/> by constant factors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This processor is registered (see <see cref="InputSystem.RegisterProcessor{T}"/>) under the name "scaleVector2".
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Double the length of the vector produced by leftStick on gamepad.
|
||||
/// myAction.AddBinding("<Gamepad>/leftStick").WithProcessor("scaleVector2(x=2,y=2)");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="ScaleProcessor"/>
|
||||
/// <seealso cref="ScaleVector3Processor"/>
|
||||
public class ScaleVector2Processor : InputProcessor<Vector2>
|
||||
{
|
||||
/// <summary>
|
||||
/// Scale factor to apply to the vector's <c>x</c> axis. Defaults to 1.
|
||||
/// </summary>
|
||||
[Tooltip("Scale factor to multiply the incoming Vector2's X component by.")]
|
||||
public float x = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Scale factor to apply to the vector's <c>y</c> axis. Defaults to 1.
|
||||
/// </summary>
|
||||
[Tooltip("Scale factor to multiply the incoming Vector2's Y component by.")]
|
||||
public float y = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Return <paramref name="value"/> scaled by <see cref="x"/> and <see cref="y"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Scaled vector.</returns>
|
||||
public override Vector2 Process(Vector2 value, InputControl control)
|
||||
{
|
||||
return new Vector2(value.x * x, value.y * y);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"ScaleVector2(x={x},y={y})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96bf34fe183cd4ce986db61a6639cd5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,57 @@
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scale the components of a <see cref="Vector3"/> by constant factors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This processor is registered (see <see cref="InputSystem.RegisterProcessor{T}"/>) under the name "scaleVector3".
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Double the magnitude of gravity values read from a gravity sensor.
|
||||
/// myAction.AddBinding("<GravitySensor>/gravity").WithProcessor("scaleVector3(x=2,y=2,z=2)");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="ScaleProcessor"/>
|
||||
/// <seealso cref="ScaleVector2Processor"/>
|
||||
public class ScaleVector3Processor : InputProcessor<Vector3>
|
||||
{
|
||||
/// <summary>
|
||||
/// Scale factor to apply to the vector's <c>x</c> axis. Defaults to 1.
|
||||
/// </summary>
|
||||
[Tooltip("Scale factor to multiply the incoming Vector3's X component by.")]
|
||||
public float x = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Scale factor to apply to the vector's <c>y</c> axis. Defaults to 1.
|
||||
/// </summary>
|
||||
[Tooltip("Scale factor to multiply the incoming Vector3's Y component by.")]
|
||||
public float y = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Scale factor to apply to the vector's <c>z</c> axis. Defaults to 1.
|
||||
/// </summary>
|
||||
[Tooltip("Scale factor to multiply the incoming Vector3's Z component by.")]
|
||||
public float z = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Return <paramref name="value"/> scaled by <see cref="x"/>, <see cref="y"/>, and <see cref="z"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">Input value.</param>
|
||||
/// <param name="control">Ignored.</param>
|
||||
/// <returns>Scaled vector.</returns>
|
||||
public override Vector3 Process(Vector3 value, InputControl control)
|
||||
{
|
||||
return new Vector3(value.x * x, value.y * y, value.z * z);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"ScaleVector3(x={x},y={y},z={z})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 681942113a3d6460a90af134a86edb78
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine.InputSystem.Editor;
|
||||
using UnityEngine.UIElements;
|
||||
#endif
|
||||
|
||||
////REVIEW: rename to RadialDeadzone
|
||||
|
||||
////TODO: add different deadzone shapes and/or option to min/max X and Y separately
|
||||
|
||||
namespace UnityEngine.InputSystem.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes a Vector2 to apply deadzoning according to the magnitude of the vector (rather
|
||||
/// than just clamping individual axes). Normalizes to the min/max range.
|
||||
/// </summary>
|
||||
/// <seealso cref="AxisDeadzoneProcessor"/>
|
||||
public class StickDeadzoneProcessor : InputProcessor<Vector2>
|
||||
{
|
||||
/// <summary>
|
||||
/// Value at which the lower bound deadzone starts.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Values in the input at or below min will get dropped and values
|
||||
/// will be scaled to the range between min and max.
|
||||
/// </remarks>
|
||||
public float min;
|
||||
public float max;
|
||||
|
||||
private float minOrDefault => min == default ? InputSystem.settings.defaultDeadzoneMin : min;
|
||||
private float maxOrDefault => max == default ? InputSystem.settings.defaultDeadzoneMax : max;
|
||||
|
||||
public override Vector2 Process(Vector2 value, InputControl control = null)
|
||||
{
|
||||
var magnitude = value.magnitude;
|
||||
var newMagnitude = GetDeadZoneAdjustedValue(magnitude);
|
||||
if (newMagnitude == 0)
|
||||
value = Vector2.zero;
|
||||
else
|
||||
value *= newMagnitude / magnitude;
|
||||
return value;
|
||||
}
|
||||
|
||||
private float GetDeadZoneAdjustedValue(float value)
|
||||
{
|
||||
var min = minOrDefault;
|
||||
var max = maxOrDefault;
|
||||
|
||||
var absValue = Mathf.Abs(value);
|
||||
if (absValue < min)
|
||||
return 0;
|
||||
if (absValue > max)
|
||||
return Mathf.Sign(value);
|
||||
|
||||
return Mathf.Sign(value) * ((absValue - min) / (max - min));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"StickDeadzone(min={minOrDefault},max={maxOrDefault})";
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal class StickDeadzoneProcessorEditor : InputParameterEditor<StickDeadzoneProcessor>
|
||||
{
|
||||
protected override void OnEnable()
|
||||
{
|
||||
m_MinSetting.Initialize("Min",
|
||||
"Vector length below which input values will be clamped. After clamping, vector lengths will be renormalized to [0..1] between min and max.",
|
||||
"Default Deadzone Min",
|
||||
() => target.min, v => target.min = v,
|
||||
() => InputSystem.settings.defaultDeadzoneMin);
|
||||
m_MaxSetting.Initialize("Max",
|
||||
"Vector length above which input values will be clamped. After clamping, vector lengths will be renormalized to [0..1] between min and max.",
|
||||
"Default Deadzone Max",
|
||||
() => target.max, v => target.max = v,
|
||||
() => InputSystem.settings.defaultDeadzoneMax);
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
|
||||
{
|
||||
m_MinSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
m_MaxSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
}
|
||||
|
||||
private CustomOrDefaultSetting m_MinSetting;
|
||||
private CustomOrDefaultSetting m_MaxSetting;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a327bafe91572467f834d0435da166fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,123 @@
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////REVIEW: expose euler angle subcontrols?
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A generic input control reading quaternion (rotation) values.
|
||||
/// </summary>
|
||||
public class QuaternionControl : InputControl<Quaternion>
|
||||
{
|
||||
// Accessing these components as individual controls usually doesn't make too much sense,
|
||||
// but having these controls allows changing the state format on the quaternion without
|
||||
// requiring the control to explicitly support the various different storage formats.
|
||||
// Also, it allows putting processors on the individual components which may be necessary
|
||||
// to properly convert the source data.
|
||||
|
||||
/// <summary>
|
||||
/// The X component of the quaternion.
|
||||
/// </summary>
|
||||
/// <value>Control representing the X component.</value>
|
||||
[InputControl(displayName = "X")]
|
||||
public AxisControl x { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Y component of the quaternion.
|
||||
/// </summary>
|
||||
/// <value>Control representing the Y component.</value>
|
||||
[InputControl(displayName = "Y")]
|
||||
public AxisControl y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Z component of the quaternion.
|
||||
/// </summary>
|
||||
/// <value>Control representing the Z component.</value>
|
||||
[InputControl(displayName = "Z")]
|
||||
public AxisControl z { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The W component of the quaternion.
|
||||
/// </summary>
|
||||
/// <value>Control representing the W component.</value>
|
||||
[InputControl(displayName = "W")]
|
||||
public AxisControl w { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default-initialize the control.
|
||||
/// </summary>
|
||||
public QuaternionControl()
|
||||
{
|
||||
m_StateBlock.sizeInBits = sizeof(float) * 4 * 8;
|
||||
m_StateBlock.format = InputStateBlock.FormatQuaternion;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
x = GetChildControl<AxisControl>("x");
|
||||
y = GetChildControl<AxisControl>("y");
|
||||
z = GetChildControl<AxisControl>("z");
|
||||
w = GetChildControl<AxisControl>("w");
|
||||
base.FinishSetup();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override unsafe Quaternion ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
switch (m_OptimizedControlDataType)
|
||||
{
|
||||
case InputStateBlock.kFormatQuaternion:
|
||||
return *(Quaternion*)((byte*)statePtr + (int)m_StateBlock.byteOffset);
|
||||
default:
|
||||
return new Quaternion(
|
||||
x.ReadValueFromStateWithCaching(statePtr),
|
||||
y.ReadValueFromStateWithCaching(statePtr),
|
||||
z.ReadValueFromStateWithCaching(statePtr),
|
||||
w.ReadUnprocessedValueFromStateWithCaching(statePtr));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override unsafe void WriteValueIntoState(Quaternion value, void* statePtr)
|
||||
{
|
||||
switch (m_OptimizedControlDataType)
|
||||
{
|
||||
case InputStateBlock.kFormatQuaternion:
|
||||
*(Quaternion*)((byte*)statePtr + (int)m_StateBlock.byteOffset) = value;
|
||||
break;
|
||||
default:
|
||||
x.WriteValueIntoState(value.x, statePtr);
|
||||
y.WriteValueIntoState(value.y, statePtr);
|
||||
z.WriteValueIntoState(value.z, statePtr);
|
||||
w.WriteValueIntoState(value.w, statePtr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override FourCC CalculateOptimizedControlDataType()
|
||||
{
|
||||
if (
|
||||
m_StateBlock.sizeInBits == sizeof(float) * 4 * 8 &&
|
||||
m_StateBlock.bitOffset == 0 &&
|
||||
x.optimizedControlDataType == InputStateBlock.FormatFloat &&
|
||||
y.optimizedControlDataType == InputStateBlock.FormatFloat &&
|
||||
z.optimizedControlDataType == InputStateBlock.FormatFloat &&
|
||||
w.optimizedControlDataType == InputStateBlock.FormatFloat &&
|
||||
y.m_StateBlock.byteOffset == x.m_StateBlock.byteOffset + 4 &&
|
||||
z.m_StateBlock.byteOffset == x.m_StateBlock.byteOffset + 8 &&
|
||||
w.m_StateBlock.byteOffset == x.m_StateBlock.byteOffset + 12 &&
|
||||
// For some unknown reason ReadUnprocessedValueFromState here uses ReadValueFromState on x/y/z (but not w)
|
||||
// which means we can't optimize if there any processors on x/y/z
|
||||
x.m_ProcessorStack.length == 0 &&
|
||||
y.m_ProcessorStack.length == 0 &&
|
||||
z.m_ProcessorStack.length == 0
|
||||
)
|
||||
return InputStateBlock.FormatQuaternion;
|
||||
|
||||
return InputStateBlock.FormatInvalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e9857b4032c493aa79e61db86f9e09b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,119 @@
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.Processors;
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A two-axis thumbstick control that can act as both a vector and a four-way dpad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Stick controls are used to represent the thumbsticks on gamepads (see <see cref="Gamepad.leftStick"/>
|
||||
/// and <see cref="Gamepad.rightStick"/>) as well as the main stick control of joysticks (see
|
||||
/// <see cref="Joystick.stick"/>).
|
||||
///
|
||||
/// Essentially, a stick is an extended <c>Vector2</c> control that can function either
|
||||
/// as a combined 2D vector, as independent vertical and horizontal axes, or as four
|
||||
/// individual, directional buttons. The following example demonstrates this based on the
|
||||
/// gamepad's left stick.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Read stick as a combined 2D vector.
|
||||
/// Gamepad.current.leftStick.ReadValue();
|
||||
///
|
||||
/// // Read X and Y axis of stick individually.
|
||||
/// Gamepad.current.leftStick.x.ReadValue();
|
||||
/// Gamepad.current.leftStick.y.ReadValue();
|
||||
///
|
||||
/// // Read the stick as four individual directional buttons.
|
||||
/// Gamepad.current.leftStick.up.ReadValue();
|
||||
/// Gamepad.current.leftStick.down.ReadValue();
|
||||
/// Gamepad.current.leftStick.left.ReadValue();
|
||||
/// Gamepad.current.leftStick.right.ReadValue();
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// In terms of memory, a stick controls is still just from one value for the X axis
|
||||
/// and one value for the Y axis.
|
||||
///
|
||||
/// Unlike dpads (see <see cref="DpadControl"/>), sticks will usually have deadzone processors
|
||||
/// (see <see cref="StickDeadzoneProcessor"/>) applied to them to get rid of noise around the
|
||||
/// resting point of the stick. The X and Y axis also have deadzones applied to them by
|
||||
/// default (<see cref="AxisDeadzoneProcessor"/>). Note, however, that the deadzoning of
|
||||
/// individual axes is different from the deadzoning applied to the stick as a whole and
|
||||
/// thus does not have to result in exactly the same values. Deadzoning of individual axes
|
||||
/// is linear (i.e. the result is simply clamped and normalized back into [0..1] range) whereas
|
||||
/// the deadzoning of sticks is radial (i.e. the length of the vector is taken into account
|
||||
/// which means that <em>both</em> the X and Y axis contribute).
|
||||
/// </remarks>
|
||||
public class StickControl : Vector2Control
|
||||
{
|
||||
////REVIEW: should X and Y have "Horizontal" and "Vertical" as long display names and "X" and "Y" as short names?
|
||||
|
||||
// Buttons for each of the directions. Allows the stick to function as a dpad.
|
||||
// Note that these controls are marked as synthetic as there isn't real buttons for the half-axes
|
||||
// on the device. This aids in interactive picking by making sure that if we have to decide between,
|
||||
// say, leftStick/x and leftStick/left, leftStick/x wins out.
|
||||
|
||||
////REVIEW: up/down/left/right should probably prohibit being written to
|
||||
////REVIEW: Should up/down/left/control actually be their own control types that *read* the values
|
||||
//// from X and Y instead of sharing their state? The current setup easily leads to various
|
||||
//// problems because more than just the state block is needed to read the value of a control
|
||||
//// from state correctly.
|
||||
|
||||
/// <summary>
|
||||
/// A synthetic button representing the upper half of the stick's Y axis, i.e. the 0 to 1 range.
|
||||
/// </summary>
|
||||
/// <value>Control representing the stick's upper half Y axis.</value>
|
||||
/// <remarks>
|
||||
/// The control is marked as <see cref="InputControl.synthetic"/>.
|
||||
/// </remarks>
|
||||
[InputControl(useStateFrom = "y", processors = "axisDeadzone", parameters = "clamp=2,clampMin=0,clampMax=1", synthetic = true, displayName = "Up")]
|
||||
// Set min&max on XY axes. We do this here as the documentation generator will not be happy
|
||||
// if we place this above the doc comment.
|
||||
// Also puts AxisDeadzones on the axes.
|
||||
[InputControl(name = "x", minValue = -1f, maxValue = 1f, layout = "Axis", processors = "axisDeadzone")]
|
||||
[InputControl(name = "y", minValue = -1f, maxValue = 1f, layout = "Axis", processors = "axisDeadzone")]
|
||||
public ButtonControl up { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A synthetic button representing the lower half of the stick's Y axis, i.e. the -1 to 0 range (inverted).
|
||||
/// </summary>
|
||||
/// <value>Control representing the stick's lower half Y axis.</value>
|
||||
/// <remarks>
|
||||
/// The control is marked as <see cref="InputControl.synthetic"/>.
|
||||
/// </remarks>
|
||||
[InputControl(useStateFrom = "y", processors = "axisDeadzone", parameters = "clamp=2,clampMin=-1,clampMax=0,invert", synthetic = true, displayName = "Down")]
|
||||
public ButtonControl down { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A synthetic button representing the left half of the stick's X axis, i.e. the -1 to 0 range (inverted).
|
||||
/// </summary>
|
||||
/// <value>Control representing the stick's left half X axis.</value>
|
||||
/// <remarks>
|
||||
/// The control is marked as <see cref="InputControl.synthetic"/>.
|
||||
/// </remarks>
|
||||
[InputControl(useStateFrom = "x", processors = "axisDeadzone", parameters = "clamp=2,clampMin=-1,clampMax=0,invert", synthetic = true, displayName = "Left")]
|
||||
public ButtonControl left { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A synthetic button representing the right half of the stick's X axis, i.e. the 0 to 1 range.
|
||||
/// </summary>
|
||||
/// <value>Control representing the stick's right half X axis.</value>
|
||||
/// <remarks>
|
||||
/// The control is marked as <see cref="InputControl.synthetic"/>.
|
||||
/// </remarks>
|
||||
[InputControl(useStateFrom = "x", processors = "axisDeadzone", parameters = "clamp=2,clampMin=0,clampMax=1", synthetic = true, displayName = "Right")]
|
||||
public ButtonControl right { get; set; }
|
||||
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
|
||||
up = GetChildControl<ButtonControl>("up");
|
||||
down = GetChildControl<ButtonControl>("down");
|
||||
left = GetChildControl<ButtonControl>("left");
|
||||
right = GetChildControl<ButtonControl>("right");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 854faad156b245d4b50a15e233e9ad55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,243 @@
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
////TODO: enforce memory layout of TouchControl to be that of TouchState (build that into the layout system? "freeze"/final layout?)
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A control representing a touch contact.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that unlike most other control types, <c>TouchControls</c> do not have
|
||||
/// a flexible memory layout. They are hardwired to <see cref="TouchState"/> and
|
||||
/// will not work correctly with a different memory layouts. Additional fields may
|
||||
/// be appended to the struct but what's there in the struct has to be located
|
||||
/// at exactly those memory addresses.
|
||||
/// </remarks>
|
||||
[InputControlLayout(stateType = typeof(TouchState))]
|
||||
public class TouchControl : InputControl<TouchState>
|
||||
{
|
||||
/// <summary>
|
||||
/// Button that indicates whether there is currently an ongoing touch
|
||||
/// contact on the control. When touch is ongoing, button will be 1,
|
||||
/// otherwise button will be 0.
|
||||
/// </summary>
|
||||
/// <value>Control representing an ongoing touch contact.</value>
|
||||
/// <remarks>
|
||||
/// This control simply monitors <see cref="phase"/> and will read as 1 whenever
|
||||
/// the phase is <see cref="TouchPhase.Began"/>, <see cref="TouchPhase.Moved"/>,
|
||||
/// or <see cref="TouchPhase.Stationary"/>.
|
||||
/// </remarks>
|
||||
/// <seealso cref="phase"/>
|
||||
public TouchPressControl press { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the display that was touched.
|
||||
/// <see href="https://docs.unity3d.com/ScriptReference/Display.html"/>
|
||||
/// </summary>
|
||||
public IntegerControl displayIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the touch contact as reported by the underlying system.
|
||||
/// </summary>
|
||||
/// <value>Control reading out the ID of the touch.</value>
|
||||
/// <remarks>
|
||||
/// Each touch contact that is made with the screen receives its own unique ID which is
|
||||
/// normally assigned by the underlying platform.
|
||||
///
|
||||
/// Note a platform may reuse touch IDs after their respective touches have finished.
|
||||
/// This means that the guarantee of uniqueness is only made with respect to currently
|
||||
/// ongoing touches.
|
||||
/// </remarks>
|
||||
/// <seealso cref="TouchState.touchId"/>
|
||||
public IntegerControl touchId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Absolute screen-space position on the touch surface.
|
||||
/// </summary>
|
||||
/// <value>Control representing the screen-space of the touch.</value>
|
||||
/// <seealso cref="TouchState.position"/>
|
||||
public Vector2Control position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Screen-space motion delta of the touch.
|
||||
/// </summary>
|
||||
/// <value>Control representing the screen-space motion delta of the touch.</value>
|
||||
/// <remarks>
|
||||
/// This is either supplied directly by the underlying platform or computed on the
|
||||
/// fly by <see cref="Touchscreen"/> from the last known position of the touch.
|
||||
///
|
||||
/// Note that deltas have behaviors attached to them different from most other
|
||||
/// controls. See <see cref="Pointer.delta"/> for details.
|
||||
/// </remarks>
|
||||
/// <seealso cref="TouchState.delta"/>
|
||||
public DeltaControl delta { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized pressure of the touch against the touch surface.
|
||||
/// </summary>
|
||||
/// <value>Control representing the pressure level of the touch.</value>
|
||||
/// <remarks>
|
||||
/// Not all touchscreens are pressure-sensitive. If unsupported, this control will remain
|
||||
/// at default value.
|
||||
///
|
||||
/// In general, touch pressure is supported on mobile platforms only.
|
||||
///
|
||||
/// Note that it is possible for the value to go above 1 even though it is considered normalized. The reason is
|
||||
/// that calibration on the system can put the maximum pressure point below the physically supported maximum value.
|
||||
/// </remarks>
|
||||
/// <seealso cref="TouchState.pressure"/>
|
||||
/// <seealso cref="Pointer.pressure"/>
|
||||
public AxisControl pressure { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Screen-space radius of the touch.
|
||||
/// </summary>
|
||||
/// <value>Control representing the horizontal and vertical extents of the touch contact.</value>
|
||||
/// <remarks>
|
||||
/// If supported by the device, this reports the size of the touch contact based on its
|
||||
/// <see cref="position"/> center point. If not supported, this will be <c>default(Vector2)</c>.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Pointer.radius"/>
|
||||
public Vector2Control radius { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current phase of the touch.
|
||||
/// </summary>
|
||||
/// <value>Control representing the current phase of the touch.</value>
|
||||
/// <remarks>
|
||||
/// This will be <see cref="TouchPhase.None"/> if no touch has been registered on the control
|
||||
/// yet or if the control has been reset to its default state.
|
||||
/// </remarks>
|
||||
/// <seealso cref="isInProgress"/>
|
||||
public TouchPhaseControl phase { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the touch comes from a source other than direct contact with the touch surface.
|
||||
/// </summary>
|
||||
/// <value>Control indicating whether the touch was generated indirectly.</value>
|
||||
/// <remarks>
|
||||
/// Indirect touches can be generated with a stylus, for example.
|
||||
/// </remarks>
|
||||
public ButtonControl indirectTouch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the touch has performed a tap.
|
||||
/// </summary>
|
||||
/// <value>Control that indicates whether the touch has tapped the screen.</value>
|
||||
/// <remarks>
|
||||
/// A tap is defined as a touch that begins and ends within <see cref="InputSettings.defaultTapTime"/> and
|
||||
/// stays within <see cref="InputSettings.tapRadius"/> of its <see cref="startPosition"/>. If this
|
||||
/// is the case for a touch, this button is set to 1 at the time the touch goes to <see cref="phase"/>
|
||||
/// <see cref="TouchPhase.Ended"/>.
|
||||
///
|
||||
/// The button resets to 0 only when another touch is started on the control or when the control
|
||||
/// is reset.
|
||||
/// </remarks>
|
||||
/// <seealso cref="tapCount"/>
|
||||
/// <seealso cref="InputSettings.defaultTapTime"/>
|
||||
public ButtonControl tap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of times that the touch has been tapped in succession.
|
||||
/// </summary>
|
||||
/// <value>Control that indicates how many taps have been performed one after the other.</value>
|
||||
/// <remarks>
|
||||
/// Successive taps have to come within <see cref="InputSettings.multiTapDelayTime"/> for them
|
||||
/// to increase the tap count. I.e. if a new tap finishes within that time after <see cref="startTime"/>
|
||||
/// of the previous touch, the tap count is increased by one. If more than <see cref="InputSettings.multiTapDelayTime"/>
|
||||
/// passes after a tap with no successive tap, the tap count is reset to zero.
|
||||
/// </remarks>
|
||||
public IntegerControl tapCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time in seconds on the same timeline as <c>Time.realTimeSinceStartup</c> when the touch began.
|
||||
/// </summary>
|
||||
/// <value>Control representing the start time of the touch.</value>
|
||||
/// <remarks>
|
||||
/// This is the value of <see cref="InputEvent.time"/> when the touch starts with
|
||||
/// <see cref="phase"/> <see cref="TouchPhase.Began"/>.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputEvent.time"/>
|
||||
public DoubleControl startTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Screen-space position where the touch started.
|
||||
/// </summary>
|
||||
/// <value>Control representing the start position of the touch.</value>
|
||||
/// <seealso cref="position"/>
|
||||
public Vector2Control startPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether a touch on the control is currently is progress.
|
||||
/// </summary>
|
||||
/// <value>If true, a touch is in progress, i.e. has a <see cref="phase"/> of
|
||||
/// <see cref="TouchPhase.Began"/>, <see cref="TouchPhase.Moved"/>, or <see
|
||||
/// cref="TouchPhase.Canceled"/>.</value>
|
||||
public bool isInProgress
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (phase.value)
|
||||
{
|
||||
case TouchPhase.Began:
|
||||
case TouchPhase.Moved:
|
||||
case TouchPhase.Stationary:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default-initialize the touch control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sets the <see cref="InputStateBlock.format"/> to <c>"TOUC"</c>.
|
||||
/// </remarks>
|
||||
public TouchControl()
|
||||
{
|
||||
m_StateBlock.format = new FourCC('T', 'O', 'U', 'C');
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
press = GetChildControl<TouchPressControl>("press");
|
||||
displayIndex = GetChildControl<IntegerControl>("displayIndex");
|
||||
touchId = GetChildControl<IntegerControl>("touchId");
|
||||
position = GetChildControl<Vector2Control>("position");
|
||||
delta = GetChildControl<DeltaControl>("delta");
|
||||
pressure = GetChildControl<AxisControl>("pressure");
|
||||
radius = GetChildControl<Vector2Control>("radius");
|
||||
phase = GetChildControl<TouchPhaseControl>("phase");
|
||||
indirectTouch = GetChildControl<ButtonControl>("indirectTouch");
|
||||
tap = GetChildControl<ButtonControl>("tap");
|
||||
tapCount = GetChildControl<IntegerControl>("tapCount");
|
||||
startTime = GetChildControl<DoubleControl>("startTime");
|
||||
startPosition = GetChildControl<Vector2Control>("startPosition");
|
||||
|
||||
////TODO: throw if state layouts of the controls doesn't match TouchState
|
||||
|
||||
base.FinishSetup();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe TouchState ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
var valuePtr = (TouchState*)((byte*)statePtr + (int)m_StateBlock.byteOffset);
|
||||
return *valuePtr;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe void WriteValueIntoState(TouchState value, void* statePtr)
|
||||
{
|
||||
var valuePtr = (TouchState*)((byte*)statePtr + (int)m_StateBlock.byteOffset);
|
||||
UnsafeUtility.MemCpy(valuePtr, UnsafeUtility.AddressOf(ref value), UnsafeUtility.SizeOf<TouchState>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 793d3fce58f547d688fdc78bee75cd30
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
// Unfortunately, C# (at least up to version 6) does not support enum type constraints. There's
|
||||
// ways to work around it in some situations (https://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum)
|
||||
// but not in a way that will allow us to convert an int to the enum type.
|
||||
|
||||
////TODO: allow this to be stored in less than 32bits
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A control reading a <see cref="TouchPhase"/> value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used mainly by <see cref="Touchscreen"/> to read <see cref="TouchState.phase"/>.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Touchscreen"/>
|
||||
[InputControlLayout(hideInUI = true)]
|
||||
public class TouchPhaseControl : InputControl<TouchPhase>
|
||||
{
|
||||
/// <summary>
|
||||
/// Default-initialize the control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Format of the control is <see cref="InputStateBlock.FormatInt"/>
|
||||
/// by default.
|
||||
/// </remarks>
|
||||
public TouchPhaseControl()
|
||||
{
|
||||
m_StateBlock.format = InputStateBlock.FormatInt;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe TouchPhase ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
var intValue = stateBlock.ReadInt(statePtr);
|
||||
return (TouchPhase)intValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe void WriteValueIntoState(TouchPhase value, void* statePtr)
|
||||
{
|
||||
var valuePtr = (byte*)statePtr + (int)m_StateBlock.byteOffset;
|
||||
*(TouchPhase*)valuePtr = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e3ecb5c3d351df489bd05ac1d820a6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A button that reads its pressed state from <see cref="TouchControl.phase"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This control is used by <see cref="TouchControl"/> to link <see cref="TouchControl.press"/>
|
||||
/// to <see cref="TouchControl.phase"/>. It will return 1 as long as the value of
|
||||
/// phase is <see cref="TouchPhase.Began"/>, <see cref="TouchPhase.Stationary"/>, or
|
||||
/// <see cref="TouchPhase.Moved"/>, i.e. as long as the touch is in progress. For
|
||||
/// all other phases, it will return 0.
|
||||
/// </remarks>
|
||||
/// <seealso cref="TouchControl"/>
|
||||
[InputControlLayout(hideInUI = true)]
|
||||
public class TouchPressControl : ButtonControl
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
|
||||
if (!stateBlock.format.IsIntegerFormat())
|
||||
throw new NotSupportedException(
|
||||
$"Non-integer format '{stateBlock.format}' is not supported for TouchButtonControl '{this}'");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe float ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
var valuePtr = (byte*)statePtr + (int)m_StateBlock.byteOffset;
|
||||
var uintValue = MemoryHelpers.ReadMultipleBitsAsUInt(valuePtr, m_StateBlock.bitOffset, m_StateBlock.sizeInBits);
|
||||
var phaseValue = (TouchPhase)uintValue;
|
||||
|
||||
var value = 0.0f;
|
||||
if (phaseValue == TouchPhase.Began || phaseValue == TouchPhase.Stationary ||
|
||||
phaseValue == TouchPhase.Moved)
|
||||
value = 1;
|
||||
|
||||
return Preprocess(value);
|
||||
}
|
||||
|
||||
public override unsafe void WriteValueIntoState(float value, void* statePtr)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3da24ab555e0c04793b0618d387a2b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,106 @@
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A floating-point 2D vector control composed of two <see cref="AxisControl"/>s.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An example is <see cref="Pointer.position"/>.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// Debug.Log(string.Format("Mouse position x={0} y={1}",
|
||||
/// Mouse.current.position.x.ReadValue(),
|
||||
/// Mouse.current.position.y.ReadValue()));
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// Normalization is not implied. The X and Y coordinates can be in any range or units.
|
||||
/// </remarks>
|
||||
public class Vector2Control : InputControl<Vector2>
|
||||
{
|
||||
/// <summary>
|
||||
/// Horizontal position of the control.
|
||||
/// </summary>
|
||||
/// <value>Control representing horizontal motion input.</value>
|
||||
[InputControl(offset = 0, displayName = "X")]
|
||||
public AxisControl x { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Vertical position of the control.
|
||||
/// </summary>
|
||||
/// <value>Control representing vertical motion input.</value>
|
||||
[InputControl(offset = 4, displayName = "Y")]
|
||||
public AxisControl y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default-initialize the control.
|
||||
/// </summary>
|
||||
public Vector2Control()
|
||||
{
|
||||
m_StateBlock.format = InputStateBlock.FormatVector2;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
x = GetChildControl<AxisControl>("x");
|
||||
y = GetChildControl<AxisControl>("y");
|
||||
|
||||
base.FinishSetup();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe Vector2 ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
switch (m_OptimizedControlDataType)
|
||||
{
|
||||
case InputStateBlock.kFormatVector2:
|
||||
return *(Vector2*)((byte*)statePtr + (int)m_StateBlock.byteOffset);
|
||||
default:
|
||||
return new Vector2(
|
||||
x.ReadUnprocessedValueFromStateWithCaching(statePtr),
|
||||
y.ReadUnprocessedValueFromStateWithCaching(statePtr));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe void WriteValueIntoState(Vector2 value, void* statePtr)
|
||||
{
|
||||
switch (m_OptimizedControlDataType)
|
||||
{
|
||||
case InputStateBlock.kFormatVector2:
|
||||
*(Vector2*)((byte*)statePtr + (int)m_StateBlock.byteOffset) = value;
|
||||
break;
|
||||
default:
|
||||
x.WriteValueIntoState(value.x, statePtr);
|
||||
y.WriteValueIntoState(value.y, statePtr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override unsafe float EvaluateMagnitude(void* statePtr)
|
||||
{
|
||||
////REVIEW: this can go beyond 1; that okay?
|
||||
return ReadValueFromStateWithCaching(statePtr).magnitude;
|
||||
}
|
||||
|
||||
protected override FourCC CalculateOptimizedControlDataType()
|
||||
{
|
||||
if (
|
||||
m_StateBlock.sizeInBits == sizeof(float) * 2 * 8 &&
|
||||
m_StateBlock.bitOffset == 0 &&
|
||||
x.optimizedControlDataType == InputStateBlock.FormatFloat &&
|
||||
y.optimizedControlDataType == InputStateBlock.FormatFloat &&
|
||||
y.m_StateBlock.byteOffset == x.m_StateBlock.byteOffset + 4
|
||||
)
|
||||
return InputStateBlock.FormatVector2;
|
||||
|
||||
return InputStateBlock.FormatInvalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06c77ad4af684d2eab4294e0d51afccd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,84 @@
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A floating-point 3D vector control composed of three <see cref="AxisControl">AxisControls</see>.
|
||||
/// </summary>
|
||||
public class Vector3Control : InputControl<Vector3>
|
||||
{
|
||||
[InputControl(offset = 0, displayName = "X")]
|
||||
public AxisControl x { get; set; }
|
||||
[InputControl(offset = 4, displayName = "Y")]
|
||||
public AxisControl y { get; set; }
|
||||
[InputControl(offset = 8, displayName = "Z")]
|
||||
public AxisControl z { get; set; }
|
||||
|
||||
public Vector3Control()
|
||||
{
|
||||
m_StateBlock.format = InputStateBlock.FormatVector3;
|
||||
}
|
||||
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
x = GetChildControl<AxisControl>("x");
|
||||
y = GetChildControl<AxisControl>("y");
|
||||
z = GetChildControl<AxisControl>("z");
|
||||
|
||||
base.FinishSetup();
|
||||
}
|
||||
|
||||
public override unsafe Vector3 ReadUnprocessedValueFromState(void* statePtr)
|
||||
{
|
||||
switch (m_OptimizedControlDataType)
|
||||
{
|
||||
case InputStateBlock.kFormatVector3:
|
||||
return *(Vector3*)((byte*)statePtr + (int)m_StateBlock.byteOffset);
|
||||
default:
|
||||
return new Vector3(
|
||||
x.ReadUnprocessedValueFromStateWithCaching(statePtr),
|
||||
y.ReadUnprocessedValueFromStateWithCaching(statePtr),
|
||||
z.ReadUnprocessedValueFromStateWithCaching(statePtr));
|
||||
}
|
||||
}
|
||||
|
||||
public override unsafe void WriteValueIntoState(Vector3 value, void* statePtr)
|
||||
{
|
||||
switch (m_OptimizedControlDataType)
|
||||
{
|
||||
case InputStateBlock.kFormatVector3:
|
||||
*(Vector3*)((byte*)statePtr + (int)m_StateBlock.byteOffset) = value;
|
||||
break;
|
||||
default:
|
||||
x.WriteValueIntoState(value.x, statePtr);
|
||||
y.WriteValueIntoState(value.y, statePtr);
|
||||
z.WriteValueIntoState(value.z, statePtr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override unsafe float EvaluateMagnitude(void* statePtr)
|
||||
{
|
||||
////REVIEW: this can go beyond 1; that okay?
|
||||
return ReadValueFromStateWithCaching(statePtr).magnitude;
|
||||
}
|
||||
|
||||
protected override FourCC CalculateOptimizedControlDataType()
|
||||
{
|
||||
if (
|
||||
m_StateBlock.sizeInBits == sizeof(float) * 3 * 8 &&
|
||||
m_StateBlock.bitOffset == 0 &&
|
||||
x.optimizedControlDataType == InputStateBlock.FormatFloat &&
|
||||
y.optimizedControlDataType == InputStateBlock.FormatFloat &&
|
||||
z.optimizedControlDataType == InputStateBlock.FormatFloat &&
|
||||
y.m_StateBlock.byteOffset == x.m_StateBlock.byteOffset + 4 &&
|
||||
z.m_StateBlock.byteOffset == x.m_StateBlock.byteOffset + 8
|
||||
)
|
||||
return InputStateBlock.FormatVector3;
|
||||
|
||||
return InputStateBlock.FormatInvalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 914fdb42cb964faf92803ee51a7279fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user