This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,248 @@
using System.ComponentModel;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Processors;
using UnityEngine.InputSystem.Utilities;
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine.InputSystem.Editor;
using UnityEngine.UIElements;
#endif
namespace UnityEngine.InputSystem.Composites
{
/// <summary>
/// A single axis value computed from one axis that pulls in the <see cref="negative"/> direction (<see cref="minValue"/>) and one
/// axis that pulls in the <see cref="positive"/> direction (<see cref="maxValue"/>).
/// </summary>
/// <remarks>
/// The limits of the axis are determined by <see cref="minValue"/> and <see cref="maxValue"/>.
/// By default, they are set to <c>[-1..1]</c>. The values can be set as parameters.
///
/// <example>
/// <code>
/// var action = new InputAction();
/// action.AddCompositeBinding("Axis(minValue=0,maxValue=2)")
/// .With("Negative", "&lt;Keyboard&gt;/a")
/// .With("Positive", "&lt;Keyboard&gt;/d");
/// </code>
/// </example>
///
/// If both axes are actuated at the same time, the behavior depends on <see cref="whichSideWins"/>.
/// By default, neither side will win (<see cref="WhichSideWins.Neither"/>) and the result
/// will be 0 (or, more precisely, the midpoint between <see cref="minValue"/> and <see cref="maxValue"/>).
/// This can be customized to make the positive side win (<see cref="WhichSideWins.Positive"/>)
/// or the negative one (<see cref="WhichSideWins.Negative"/>).
///
/// This is useful, for example, in a driving game where break should cancel out accelerate.
/// By binding <see cref="negative"/> to the break control(s) and <see cref="positive"/> to the
/// acceleration control(s), and setting <see cref="whichSideWins"/> to <see cref="WhichSideWins.Negative"/>,
/// if the break button is pressed, it will always cause the acceleration button to be ignored.
///
/// The actual <em>absolute</em> values of <see cref="negative"/> and <see cref="positive"/> are used
/// to scale <see cref="minValue"/> and <see cref="maxValue"/> respectively. So if, for example, <see cref="positive"/>
/// is bound to <see cref="Gamepad.rightTrigger"/> and the trigger is at a value of 0.5, then the resulting
/// value is <c>maxValue * 0.5</c> (the actual formula is <c>midPoint + (maxValue - midPoint) * positive</c>).
/// </remarks>
[DisplayStringFormat("{negative}/{positive}")]
[DisplayName("Positive/Negative Binding")]
public class AxisComposite : InputBindingComposite<float>
{
/// <summary>
/// Binding for the axis input that controls the negative [<see cref="minValue"/>..0] direction of the
/// combined axis.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int negative = 0;
/// <summary>
/// Binding for the axis input that controls the positive [0..<see cref="maxValue"/>] direction of the
/// combined axis.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int positive = 0;
/// <summary>
/// The lower bound that the axis is limited to. -1 by default.
/// </summary>
/// <remarks>
/// This value corresponds to the full actuation of the control(s) bound to <see cref="negative"/>.
///
/// <example>
/// <code>
/// var action = new InputAction();
/// action.AddCompositeBinding("Axis(minValue=0,maxValue=2)")
/// .With("Negative", "&lt;Keyboard&gt;/a")
/// .With("Positive", "&lt;Keyboard&gt;/d");
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="maxValue"/>
/// <seealso cref="negative"/>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[Tooltip("Value to return when the negative side is fully actuated.")]
public float minValue = -1;
/// <summary>
/// The upper bound that the axis is limited to. 1 by default.
/// </summary>
/// <remarks>
/// This value corresponds to the full actuation of the control(s) bound to <see cref="positive"/>.
///
/// <example>
/// <code>
/// var action = new InputAction();
/// action.AddCompositeBinding("Axis(minValue=0,maxValue=2)")
/// .With("Negative", "&lt;Keyboard&gt;/a")
/// .With("Positive", "&lt;Keyboard&gt;/d");
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="minValue"/>
/// <seealso cref="positive"/>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[Tooltip("Value to return when the positive side is fully actuated.")]
public float maxValue = 1;
/// <summary>
/// If both the <see cref="positive"/> and <see cref="negative"/> button are actuated, this
/// determines which value is returned from the composite.
/// </summary>
[Tooltip("If both the positive and negative side are actuated, decides what value to return. 'Neither' (default) means that " +
"the resulting value is the midpoint between min and max. 'Positive' means that max will be returned. 'Negative' means that " +
"min will be returned.")]
public WhichSideWins whichSideWins = WhichSideWins.Neither;
/// <summary>
/// The value that is returned if the composite is in a neutral position, that is, if
/// neither <see cref="positive"/> nor <see cref="negative"/> are actuated or if
/// <see cref="whichSideWins"/> is set to <see cref="WhichSideWins.Neither"/> and
/// both <see cref="positive"/> and <see cref="negative"/> are actuated.
/// </summary>
public float midPoint => (maxValue + minValue) / 2;
////TODO: add parameters to control ramp up&down
/// <inheritdoc />
public override float ReadValue(ref InputBindingCompositeContext context)
{
var negativeValue = Mathf.Abs(context.ReadValue<float>(negative));
var positiveValue = Mathf.Abs(context.ReadValue<float>(positive));
var negativeIsActuated = negativeValue > Mathf.Epsilon;
var positiveIsActuated = positiveValue > Mathf.Epsilon;
if (negativeIsActuated == positiveIsActuated)
{
switch (whichSideWins)
{
case WhichSideWins.Negative:
positiveIsActuated = false;
break;
case WhichSideWins.Positive:
negativeIsActuated = false;
break;
case WhichSideWins.Neither:
return midPoint;
}
}
var mid = midPoint;
if (negativeIsActuated)
return mid - (mid - minValue) * negativeValue;
return mid + (maxValue - mid) * positiveValue;
}
/// <inheritdoc />
public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
{
var value = ReadValue(ref context);
if (value < midPoint)
{
value = Mathf.Abs(value - midPoint);
return NormalizeProcessor.Normalize(value, 0, Mathf.Abs(minValue), 0);
}
value = Mathf.Abs(value - midPoint);
return NormalizeProcessor.Normalize(value, 0, Mathf.Abs(maxValue), 0);
}
/// <summary>
/// What happens to the value of an <see cref="AxisComposite"/> if both <see cref="positive"/>
/// and <see cref="negative"/> are actuated at the same time.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1717:OnlyFlagsEnumsShouldHavePluralNames", Justification = "False positive: `Wins` is not a plural form.")]
public enum WhichSideWins
{
/// <summary>
/// If both <see cref="positive"/> and <see cref="negative"/> are actuated, the sides cancel
/// each other out and the result is 0.
/// </summary>
Neither = 0,
/// <summary>
/// If both <see cref="positive"/> and <see cref="negative"/> are actuated, the value of
/// <see cref="positive"/> wins and <see cref="negative"/> is ignored.
/// </summary>
Positive = 1,
/// <summary>
/// If both <see cref="positive"/> and <see cref="negative"/> are actuated, the value of
/// <see cref="negative"/> wins and <see cref="positive"/> is ignored.
/// </summary>
Negative = 2,
}
}
#if UNITY_EDITOR
internal class AxisCompositeEditor : InputParameterEditor<AxisComposite>
{
private GUIContent m_WhichAxisWinsLabel = new GUIContent("Which Side Wins",
"Determine which axis 'wins' if both are actuated at the same time. "
+ "If 'Neither' is selected, the result is 0 (or, more precisely, "
+ "the midpoint between minValue and maxValue).");
public override void OnGUI()
{
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseIMGUIEditorForAssets)) return;
#endif
target.whichSideWins = (AxisComposite.WhichSideWins)EditorGUILayout.EnumPopup(m_WhichAxisWinsLabel, target.whichSideWins);
}
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
{
var modeField = new EnumField(m_WhichAxisWinsLabel.text, target.whichSideWins)
{
tooltip = m_WhichAxisWinsLabel.tooltip
};
modeField.RegisterValueChangedCallback(evt =>
{
target.whichSideWins = (AxisComposite.WhichSideWins)evt.newValue;
onChangedCallback();
});
root.Add(modeField);
}
#endif
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61fdc882d66f0f34d90450c001c0078e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,137 @@
using System.ComponentModel;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.Scripting;
////TODO: remove this once we can break the API
namespace UnityEngine.InputSystem.Composites
{
/// <summary>
/// A button with an additional modifier. The button only triggers when
/// the modifier is pressed.
/// </summary>
/// <remarks>
/// This composite can be used to require another button to be held while
/// pressing the button that triggers the action. This is most commonly used
/// on keyboards to require one of the modifier keys (shift, ctrl, or alt)
/// to be held in combination with another key, e.g. "CTRL+1".
///
/// <example>
/// <code>
/// // Create a button action that triggers when CTRL+1
/// // is pressed on the keyboard.
/// var action = new InputAction(type: InputActionType.Button);
/// action.AddCompositeBinding("ButtonWithOneModifier")
/// .With("Modifier", "&lt;Keyboard&gt;/leftCtrl")
/// .With("Modifier", "&lt;Keyboard&gt;/rightControl")
/// .With("Button", "&lt;Keyboard&gt;/1")
/// </code>
/// </example>
///
/// Note that this is not restricted to the keyboard and will preserve
/// the full value of the button.
///
/// <example>
/// <code>
/// // Create a button action that requires the A button on the
/// // gamepad to be held and will then trigger from the gamepad's
/// // left trigger button.
/// var action = new InputAction(type: InputActionType.Button);
/// action.AddCompositeBinding("ButtonWithOneModifier")
/// .With("Modifier", "&lt;Gamepad&gt;/buttonSouth")
/// .With("Button", "&lt;Gamepad&gt;/leftTrigger");
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="ButtonWithTwoModifiers"/>
[DesignTimeVisible(false)] // Obsoleted by OneModifierComposite
[DisplayStringFormat("{modifier}+{button}")]
public class ButtonWithOneModifier : InputBindingComposite<float>
{
/// <summary>
/// Binding for the button that acts as a modifier, e.g. <c>&lt;Keyboard/leftCtrl</c>.
/// </summary>
/// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
// ReSharper disable once UnassignedField.Global
[InputControl(layout = "Button")] public int modifier;
/// <summary>
/// Binding for the button that is gated by the modifier. The composite will assume the value
/// of this button while the modifier is pressed.
/// </summary>
/// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
// ReSharper disable once UnassignedField.Global
[InputControl(layout = "Button")] public int button;
/// <summary>
/// If set to <c>true</c>, <see cref="modifier"/> can be pressed after <see cref="button"/> and the composite will
/// still trigger. Default is false.
/// </summary>
/// <remarks>
/// By default, <see cref="modifier"/> is required to be in pressed state before or at the same time that <see cref="button"/>
/// goes into pressed state for the composite as a whole to trigger. This means that binding to, for example, <c>Shift+B</c>,
/// the <c>shift</c> key has to be pressed before pressing the <c>B</c> key. This is the behavior usually expected with
/// keyboard shortcuts.
///
/// This parameter can be used to bypass this behavior and allow any timing between <see cref="modifier"/> and <see cref="button"/>.
/// The only requirement is for them both to concurrently be in pressed state.
/// </remarks>
public bool overrideModifiersNeedToBePressedFirst;
/// <summary>
/// Return the value of the <see cref="button"/> part if <see cref="modifier"/> is pressed. Otherwise
/// return 0.
/// </summary>
/// <param name="context">Evaluation context passed in from the input system.</param>
/// <returns>The current value of the composite.</returns>
public override float ReadValue(ref InputBindingCompositeContext context)
{
if (ModifierIsPressed(ref context))
return context.ReadValue<float>(button);
return default;
}
private bool ModifierIsPressed(ref InputBindingCompositeContext context)
{
var modifierDown = context.ReadValueAsButton(modifier);
if (modifierDown && !overrideModifiersNeedToBePressedFirst)
{
var timestamp = context.GetPressTime(button);
var timestamp1 = context.GetPressTime(modifier);
return timestamp1 <= timestamp;
}
return modifierDown;
}
/// <summary>
/// Same as <see cref="ReadValue"/> in this case.
/// </summary>
/// <param name="context">Evaluation context passed in from the input system.</param>
/// <returns>A >0 value if the composite is currently actuated.</returns>
public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
{
return ReadValue(ref context);
}
protected override void FinishSetup(ref InputBindingCompositeContext context)
{
if (!overrideModifiersNeedToBePressedFirst)
overrideModifiersNeedToBePressedFirst = !InputSystem.settings.shortcutKeysConsumeInput;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15164829aab964eedaee6bac785c2c05
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,153 @@
using System.ComponentModel;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.Scripting;
////TODO: remove this once we can break the API
namespace UnityEngine.InputSystem.Composites
{
/// <summary>
/// A button with two additional modifiers. The button only triggers when
/// both modifiers are pressed.
/// </summary>
/// <remarks>
/// This composite can be used to require two other buttons to be held while
/// using the control that triggers the action. This is most commonly used
/// on keyboards to require two of the modifier keys (shift, ctrl, or alt)
/// to be held in combination with another key, e.g. "CTRL+SHIFT+1".
///
/// <example>
/// <code>
/// // Create a button action that triggers when CTRL+SHIFT+1
/// // is pressed on the keyboard.
/// var action = new InputAction(type: InputActionType.Button);
/// action.AddCompositeBinding("TwoModifiers")
/// .With("Modifier1", "&lt;Keyboard&gt;/leftCtrl")
/// .With("Modifier1", "&lt;Keyboard&gt;/rightCtrl")
/// .With("Modifier2", "&lt;Keyboard&gt;/leftShift")
/// .With("Modifier2", "&lt;Keyboard&gt;/rightShift")
/// .With("Button", "&lt;Keyboard&gt;/1")
/// </code>
/// </example>
///
/// Note that this is not restricted to the keyboard and will preserve
/// the full value of the button.
///
/// <example>
/// <code>
/// // Create a button action that requires the A and X button on the
/// // gamepad to be held and will then trigger from the gamepad's
/// // left trigger button.
/// var action = new InputAction(type: InputActionType.Button);
/// action.AddCompositeBinding("ButtonWithTwoModifiers")
/// .With("Modifier1", "&lt;Gamepad&gt;/buttonSouth")
/// .With("Modifier2", "&lt;Gamepad&gt;/buttonWest")
/// .With("Button", "&lt;Gamepad&gt;/leftTrigger");
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="ButtonWithOneModifier"/>
[DesignTimeVisible(false)] // Obsoleted by TwoModifiersComposite
[DisplayStringFormat("{modifier1}+{modifier2}+{button}")]
public class ButtonWithTwoModifiers : InputBindingComposite<float>
{
/// <summary>
/// Binding for the first button that acts as a modifier, e.g. <c>&lt;Keyboard/leftCtrl</c>.
/// </summary>
/// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
// ReSharper disable once UnassignedField.Global
[InputControl(layout = "Button")] public int modifier1;
/// <summary>
/// Binding for the second button that acts as a modifier, e.g. <c>&lt;Keyboard/leftCtrl</c>.
/// </summary>
/// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
// ReSharper disable once UnassignedField.Global
[InputControl(layout = "Button")] public int modifier2;
/// <summary>
/// Binding for the button that is gated by <see cref="modifier1"/> and <see cref="modifier2"/>.
/// The composite will assume the value of this button while both of the modifiers are pressed.
/// </summary>
/// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
// ReSharper disable once UnassignedField.Global
[InputControl(layout = "Button")] public int button;
/// <summary>
/// If set to <c>true</c>, <see cref="modifier1"/> and/or <see cref="modifier2"/> can be pressed after <see cref="button"/>
/// and the composite will still trigger. Default is false.
/// </summary>
/// <remarks>
/// By default, <see cref="modifier1"/> and <see cref="modifier2"/> are required to be in pressed state before or at the same
/// time that <see cref="button"/> goes into pressed state for the composite as a whole to trigger. This means that binding to,
/// for example, <c>Ctrl+Shift+B</c>, the <c>ctrl</c> <c>shift</c> keys have to be pressed before pressing the <c>B</c> key.
/// This is the behavior usually expected with keyboard shortcuts.
///
/// This parameter can be used to bypass this behavior and allow any timing between <see cref="modifier1"/>, <see cref="modifier2"/>,
/// and <see cref="button"/>. The only requirement is for all of them to concurrently be in pressed state.
/// </remarks>
public bool overrideModifiersNeedToBePressedFirst;
/// <summary>
/// Return the value of the <see cref="button"/> part while both <see cref="modifier1"/> and <see cref="modifier2"/>
/// are pressed. Otherwise return 0.
/// </summary>
/// <param name="context">Evaluation context passed in from the input system.</param>
/// <returns>The current value of the composite.</returns>
public override float ReadValue(ref InputBindingCompositeContext context)
{
if (ModifiersArePressed(ref context))
return context.ReadValue<float>(button);
return default;
}
private bool ModifiersArePressed(ref InputBindingCompositeContext context)
{
var modifiersDown = context.ReadValueAsButton(modifier1) && context.ReadValueAsButton(modifier2);
if (modifiersDown && !overrideModifiersNeedToBePressedFirst)
{
var timestamp = context.GetPressTime(button);
var timestamp1 = context.GetPressTime(modifier1);
var timestamp2 = context.GetPressTime(modifier2);
return timestamp1 <= timestamp && timestamp2 <= timestamp;
}
return modifiersDown;
}
/// <summary>
/// Same as <see cref="ReadValue"/> in this case.
/// </summary>
/// <param name="context">Evaluation context passed in from the input system.</param>
/// <returns>A >0 value if the composite is currently actuated.</returns>
public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
{
return ReadValue(ref context);
}
protected override void FinishSetup(ref InputBindingCompositeContext context)
{
if (!overrideModifiersNeedToBePressedFirst)
overrideModifiersNeedToBePressedFirst = !InputSystem.settings.shortcutKeysConsumeInput;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 62dcc18c42c2246bdaed7fe210b77118
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,184 @@
using System;
using System.ComponentModel;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.Scripting;
////TODO: allow making modifier optional; maybe alter the value (e.g. 0=unpressed, 0.5=pressed without modifier, 1=pressed with modifier)
namespace UnityEngine.InputSystem.Composites
{
/// <summary>
/// A binding with an additional modifier. The bound controls only trigger when
/// the modifier is pressed.
/// </summary>
/// <remarks>
/// This composite can be used to require a button to be held in order to "activate"
/// another binding. This is most commonly used on keyboards to require one of the
/// modifier keys (shift, ctrl, or alt) to be held in combination with another control,
/// e.g. "CTRL+1".
///
/// <example>
/// <code>
/// // Create a button action that triggers when CTRL+1
/// // is pressed on the keyboard.
/// var action = new InputAction(type: InputActionType.Button);
/// action.AddCompositeBinding("OneModifier")
/// .With("Modifier", "&lt;Keyboard&gt;/ctrl")
/// .With("Binding", "&lt;Keyboard&gt;/1")
/// </code>
/// </example>
///
/// However, this can also be used to "gate" other types of controls. For example, a "look"
/// action could be bound to mouse <see cref="Pointer.delta"/> such that the <see cref="Keyboard.altKey"/> on the
/// keyboard has to be pressed in order for the player to be able to look around.
///
/// <example>
/// <code>
/// lookAction.AddCompositeBinding("OneModifier")
/// .With("Modifier", "&lt;Keyboard&gt;/alt")
/// .With("Binding", "&lt;Mouse&gt;/delta")
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="TwoModifiersComposite"/>
[DisplayStringFormat("{modifier}+{binding}")]
[DisplayName("Binding With One Modifier")]
public class OneModifierComposite : InputBindingComposite
{
/// <summary>
/// Binding for the button that acts as a modifier, e.g. <c>&lt;Keyboard/ctrl</c>.
/// </summary>
/// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
// ReSharper disable once UnassignedField.Global
[InputControl(layout = "Button")] public int modifier;
/// <summary>
/// Binding for the control that is gated by the modifier. The composite will assume the value
/// of this control while the modifier is considered pressed (that is, has a magnitude equal to or
/// greater than the button press point).
/// </summary>
/// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
// ReSharper disable once UnassignedField.Global
[InputControl] public int binding;
/// <summary>
/// Type of values read from controls bound to <see cref="binding"/>.
/// </summary>
public override Type valueType => m_ValueType;
/// <summary>
/// Size of the largest value that may be read from the controls bound to <see cref="binding"/>.
/// </summary>
public override int valueSizeInBytes => m_ValueSizeInBytes;
/// <summary>
/// If set to <c>true</c>, the built-in logic to determine if modifiers need to be pressed first is overridden.
/// Default value is <c>false</c>.
/// </summary>
/// <remarks>
/// By default, if <see cref="binding"/> is bound to only <see cref="Controls.ButtonControl"/>s, then the composite requires
/// <see cref="modifier"/> to be pressed <em>before</em> pressing <see cref="binding"/>. This means that binding to, for example,
/// <c>Ctrl+B</c>, the <c>ctrl</c> keys have to be pressed before pressing the <c>B</c> key. This is the behavior usually expected
/// with keyboard shortcuts.
///
/// However, when binding, for example, <c>Ctrl+MouseDelta</c>, it should be possible to press <c>ctrl</c> at any time. The default
/// logic will automatically detect the difference between this binding and the button binding in the example above and behave
/// accordingly.
///
/// This field allows you to explicitly override this default inference and make it so that regardless of what <see cref="binding"/>
/// is bound to, any press sequence is acceptable. For the example binding to <c>Ctrl+B</c>, it would mean that pressing <c>B</c> and
/// only then pressing <c>Ctrl</c> will still trigger the binding.
/// </remarks>
public bool overrideModifiersNeedToBePressedFirst;
private int m_ValueSizeInBytes;
private Type m_ValueType;
private bool m_BindingIsButton;
public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
{
if (ModifierIsPressed(ref context))
return context.EvaluateMagnitude(binding);
return default;
}
/// <inheritdoc/>
public override unsafe void ReadValue(ref InputBindingCompositeContext context, void* buffer, int bufferSize)
{
if (ModifierIsPressed(ref context))
context.ReadValue(binding, buffer, bufferSize);
else
UnsafeUtility.MemClear(buffer, m_ValueSizeInBytes);
}
private bool ModifierIsPressed(ref InputBindingCompositeContext context)
{
var modifierDown = context.ReadValueAsButton(modifier);
// When the modifiers are gating a button, we require the modifiers to be pressed *first*.
if (modifierDown && m_BindingIsButton && !overrideModifiersNeedToBePressedFirst)
{
var timestamp = context.GetPressTime(binding);
var timestamp1 = context.GetPressTime(modifier);
return timestamp1 <= timestamp;
}
return modifierDown;
}
/// <inheritdoc/>
protected override void FinishSetup(ref InputBindingCompositeContext context)
{
DetermineValueTypeAndSize(ref context, binding, out m_ValueType, out m_ValueSizeInBytes, out m_BindingIsButton);
if (!overrideModifiersNeedToBePressedFirst)
overrideModifiersNeedToBePressedFirst = !InputSystem.settings.shortcutKeysConsumeInput;
}
public override object ReadValueAsObject(ref InputBindingCompositeContext context)
{
if (context.ReadValueAsButton(modifier))
return context.ReadValueAsObject(binding);
return null;
}
internal static void DetermineValueTypeAndSize(ref InputBindingCompositeContext context, int part, out Type valueType, out int valueSizeInBytes, out bool isButton)
{
valueSizeInBytes = 0;
isButton = true;
Type type = null;
foreach (var control in context.controls)
{
if (control.part != part)
continue;
var controlType = control.control.valueType;
if (type == null || controlType.IsAssignableFrom(type))
type = controlType;
else if (!type.IsAssignableFrom(controlType))
type = typeof(Object);
valueSizeInBytes = Math.Max(control.control.valueSizeInBytes, valueSizeInBytes);
// *All* bound controls need to be buttons for us to classify this part as a "Button" part.
isButton &= control.control.isButton;
}
valueType = type;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1a0a9c8a3d9c4893ab5389e009563314
timeCreated: 1588685554

View File

@@ -0,0 +1,171 @@
using System;
using System.ComponentModel;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;
namespace UnityEngine.InputSystem.Composites
{
/// <summary>
/// A binding with two additional modifiers modifier. The bound controls only trigger when
/// both modifiers are pressed.
/// </summary>
/// <remarks>
/// This composite can be used to require two buttons to be held in order to "activate"
/// another binding. This is most commonly used on keyboards to require two of the
/// modifier keys (shift, ctrl, or alt) to be held in combination with another control,
/// e.g. "SHIFT+CTRL+1".
///
/// <example>
/// <code>
/// // Create a button action that triggers when SHIFT+CTRL+1
/// // is pressed on the keyboard.
/// var action = new InputAction(type: InputActionType.Button);
/// action.AddCompositeBinding("TwoModifiers")
/// .With("Modifier", "&lt;Keyboard&gt;/ctrl")
/// .With("Modifier", "&lt;Keyboard&gt;/shift")
/// .With("Binding", "&lt;Keyboard&gt;/1")
/// </code>
/// </example>
///
/// However, this can also be used to "gate" other types of controls. For example, a "look"
/// action could be bound to mouse <see cref="Pointer.delta"/> such that the <see cref="Keyboard.ctrlKey"/> and
/// <see cref="Keyboard.shiftKey"/> on the keyboard have to be pressed in order for the player to be able to
/// look around.
///
/// <example>
/// <code>
/// var action = new InputAction();
/// action.AddCompositeBinding("TwoModifiers")
/// .With("Modifier1", "&lt;Keyboard&gt;/ctrl")
/// .With("Modifier2", "&lt;Keyboard&gt;/shift")
/// .With("Binding", "&lt;Mouse&gt;/delta");
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="OneModifierComposite"/>
[DisplayStringFormat("{modifier1}+{modifier2}+{binding}")]
[DisplayName("Binding With Two Modifiers")]
public class TwoModifiersComposite : InputBindingComposite
{
/// <summary>
/// Binding for the first button that acts as a modifier, e.g. <c>&lt;Keyboard/leftCtrl</c>.
/// </summary>
/// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
// ReSharper disable once UnassignedField.Global
[InputControl(layout = "Button")] public int modifier1;
/// <summary>
/// Binding for the second button that acts as a modifier, e.g. <c>&lt;Keyboard/leftCtrl</c>.
/// </summary>
/// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
// ReSharper disable once UnassignedField.Global
[InputControl(layout = "Button")] public int modifier2;
/// <summary>
/// Binding for the control that is gated by <see cref="modifier1"/> and <see cref="modifier2"/>.
/// The composite will assume the value of this button while both of the modifiers are pressed.
/// </summary>
/// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
// ReSharper disable once UnassignedField.Global
[InputControl] public int binding;
/// <summary>
/// If set to <c>true</c>, the built-in logic to determine if modifiers need to be pressed first is overridden.
/// Default value is <c>false</c>.
/// </summary>
/// <remarks>
/// By default, if <see cref="binding"/> is bound to only <see cref="Controls.ButtonControl"/>s, then the composite requires
/// both <see cref="modifier1"/> and <see cref="modifier2"/> to be pressed <em>before</em> pressing <see cref="binding"/>.
/// This means that binding to, for example, <c>Ctrl+Shift+B</c>, the <c>ctrl</c> and <c>shift</c> keys have to be pressed
/// before pressing the <c>B</c> key. This is the behavior usually expected with keyboard shortcuts.
///
/// However, when binding, for example, <c>Ctrl+Shift+MouseDelta</c>, it should be possible to press <c>ctrl</c> and <c>shift</c>
/// at any time and in any order. The default logic will automatically detect the difference between this binding and the button
/// binding in the example above and behave accordingly.
///
/// This field allows you to explicitly override this default inference and make it so that regardless of what <see cref="binding"/>
/// is bound to, any press sequence is acceptable. For the example binding to <c>Ctrl+Shift+B</c>, it would mean that pressing
/// <c>B</c> and only then pressing <c>Ctrl</c> and <c>Shift</c> will still trigger the binding.
/// </remarks>
public bool overrideModifiersNeedToBePressedFirst;
/// <summary>
/// Type of values read from controls bound to <see cref="binding"/>.
/// </summary>
public override Type valueType => m_ValueType;
/// <summary>
/// Size of the largest value that may be read from the controls bound to <see cref="binding"/>.
/// </summary>
public override int valueSizeInBytes => m_ValueSizeInBytes;
private int m_ValueSizeInBytes;
private Type m_ValueType;
private bool m_BindingIsButton;
public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
{
if (ModifiersArePressed(ref context))
return context.EvaluateMagnitude(binding);
return default;
}
/// <inheritdoc/>
public override unsafe void ReadValue(ref InputBindingCompositeContext context, void* buffer, int bufferSize)
{
if (ModifiersArePressed(ref context))
context.ReadValue(binding, buffer, bufferSize);
else
UnsafeUtility.MemClear(buffer, m_ValueSizeInBytes);
}
private bool ModifiersArePressed(ref InputBindingCompositeContext context)
{
var modifiersDown = context.ReadValueAsButton(modifier1) && context.ReadValueAsButton(modifier2);
// When the modifiers are gating a button, we require the modifiers to be pressed *first*.
if (modifiersDown && m_BindingIsButton && !overrideModifiersNeedToBePressedFirst)
{
var timestamp = context.GetPressTime(binding);
var timestamp1 = context.GetPressTime(modifier1);
var timestamp2 = context.GetPressTime(modifier2);
return timestamp1 <= timestamp && timestamp2 <= timestamp;
}
return modifiersDown;
}
/// <inheritdoc/>
protected override void FinishSetup(ref InputBindingCompositeContext context)
{
OneModifierComposite.DetermineValueTypeAndSize(ref context, binding, out m_ValueType, out m_ValueSizeInBytes, out m_BindingIsButton);
if (!overrideModifiersNeedToBePressedFirst)
overrideModifiersNeedToBePressedFirst = !InputSystem.settings.shortcutKeysConsumeInput;
}
public override object ReadValueAsObject(ref InputBindingCompositeContext context)
{
if (context.ReadValueAsButton(modifier1) && context.ReadValueAsButton(modifier2))
return context.ReadValueAsObject(binding);
return null;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: be272fdf97df4e8daf1393dabdfa4cb8
timeCreated: 1588685577

View File

@@ -0,0 +1,228 @@
using System;
using System.ComponentModel;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.InputSystem.Editor;
using UnityEngine.UIElements;
#endif
////TODO: add support for ramp up/down
namespace UnityEngine.InputSystem.Composites
{
/// <summary>
/// A 2D planar motion vector computed from an up+down button pair and a left+right
/// button pair.
/// </summary>
/// <remarks>
/// This composite allows to grab arbitrary buttons from a device and arrange them in
/// a D-Pad like configuration. Based on button presses, the composite will return a
/// normalized direction vector (normalization can be turned off via <see cref="mode"/>).
///
/// Opposing motions cancel each other out. This means that if, for example, both the left
/// and right horizontal button are pressed, the resulting horizontal movement value will
/// be zero.
///
/// <example>
/// <code>
/// // Set up WASD style keyboard controls.
/// action.AddCompositeBinding("2DVector")
/// .With("Up", "&lt;Keyboard&gt;/w")
/// .With("Left", "&lt;Keyboard&gt;/a")
/// .With("Down", "&lt;Keyboard&gt;/s")
/// .With("Right", "&lt;Keyboard&gt;/d");
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="Vector3Composite"/>
[DisplayStringFormat("{up}/{left}/{down}/{right}")] // This results in WASD.
[DisplayName("Up/Down/Left/Right Composite")]
public class Vector2Composite : InputBindingComposite<Vector2>
{
/// <summary>
/// Binding for the button that represents the up (that is, <c>(0,1)</c>) direction of the vector.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int up;
/// <summary>
/// Binding for the button represents the down (that is, <c>(0,-1)</c>) direction of the vector.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int down;
/// <summary>
/// Binding for the button represents the left (that is, <c>(-1,0)</c>) direction of the vector.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int left;
/// <summary>
/// Binding for the button that represents the right (that is, <c>(1,0)</c>) direction of the vector.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
[InputControl(layout = "Axis")] public int right;
[Obsolete("Use Mode.DigitalNormalized with 'mode' instead")]
public bool normalize = true;
/// <summary>
/// How to synthesize a <c>Vector2</c> from the values read from <see cref="up"/>, <see cref="down"/>,
/// <see cref="left"/>, and <see cref="right"/>.
/// </summary>
/// <value>Determines how X and Y of the resulting <c>Vector2</c> are formed from input values.</value>
/// <remarks>
/// <example>
/// <code>
/// var action = new InputAction();
///
/// // DigitalNormalized composite (the default). Turns gamepad left stick into
/// // control equivalent to the D-Pad.
/// action.AddCompositeBinding("2DVector(mode=0)")
/// .With("up", "&lt;Gamepad&gt;/leftStick/up")
/// .With("down", "&lt;Gamepad&gt;/leftStick/down")
/// .With("left", "&lt;Gamepad&gt;/leftStick/left")
/// .With("right", "&lt;Gamepad&gt;/leftStick/right");
///
/// // Digital composite. Turns gamepad left stick into control equivalent
/// // to the D-Pad except that diagonals will not be normalized.
/// action.AddCompositeBinding("2DVector(mode=1)")
/// .With("up", "&lt;Gamepad&gt;/leftStick/up")
/// .With("down", "&lt;Gamepad&gt;/leftStick/down")
/// .With("left", "&lt;Gamepad&gt;/leftStick/left")
/// .With("right", "&lt;Gamepad&gt;/leftStick/right");
///
/// // Analog composite. In this case results in setup that behaves exactly
/// // the same as leftStick already does. But you could use it, for example,
/// // to swap directions by binding "up" to leftStick/down and "down" to
/// // leftStick/up.
/// action.AddCompositeBinding("2DVector(mode=2)")
/// .With("up", "&lt;Gamepad&gt;/leftStick/up")
/// .With("down", "&lt;Gamepad&gt;/leftStick/down")
/// .With("left", "&lt;Gamepad&gt;/leftStick/left")
/// .With("right", "&lt;Gamepad&gt;/leftStick/right");
/// </code>
/// </example>
/// </remarks>
public Mode mode;
/// <inheritdoc />
public override Vector2 ReadValue(ref InputBindingCompositeContext context)
{
var mode = this.mode;
if (mode == Mode.Analog)
{
var upValue = context.ReadValue<float>(up);
var downValue = context.ReadValue<float>(down);
var leftValue = context.ReadValue<float>(left);
var rightValue = context.ReadValue<float>(right);
return DpadControl.MakeDpadVector(upValue, downValue, leftValue, rightValue);
}
var upIsPressed = context.ReadValueAsButton(up);
var downIsPressed = context.ReadValueAsButton(down);
var leftIsPressed = context.ReadValueAsButton(left);
var rightIsPressed = context.ReadValueAsButton(right);
// Legacy. We need to reference the obsolete member here so temporarily
// turn of the warning.
#pragma warning disable CS0618
if (!normalize) // Was on by default.
mode = Mode.Digital;
#pragma warning restore CS0618
return DpadControl.MakeDpadVector(upIsPressed, downIsPressed, leftIsPressed, rightIsPressed, mode == Mode.DigitalNormalized);
}
/// <inheritdoc />
public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
{
var value = ReadValue(ref context);
return value.magnitude;
}
/// <summary>
/// Determines how a <c>Vector2</c> is synthesized from part controls.
/// </summary>
public enum Mode
{
/// <summary>
/// Part controls are treated as analog meaning that the floating-point values read from controls
/// will come through as is (minus the fact that the down and left direction values are negated).
/// </summary>
Analog = 2,
/// <summary>
/// Part controls are treated as buttons (on/off) and the resulting vector is normalized. This means
/// that if, for example, both left and up are pressed, instead of returning a vector (-1,1), a vector
/// of roughly (-0.7,0.7) (that is, corresponding to <c>new Vector2(-1,1).normalized</c>) is returned instead.
/// The resulting 2D area is diamond-shaped.
/// </summary>
DigitalNormalized = 0,
/// <summary>
/// Part controls are treated as buttons (on/off) and the resulting vector is not normalized. This means
/// that if, for example, both left and up are pressed, the resulting vector is (-1,1) and has a length
/// greater than 1. The resulting 2D area is box-shaped.
/// </summary>
Digital = 1
}
}
#if UNITY_EDITOR
internal class Vector2CompositeEditor : InputParameterEditor<Vector2Composite>
{
private GUIContent m_ModeLabel = new GUIContent("Mode",
"How to synthesize a Vector2 from the inputs. Digital "
+ "treats part bindings as buttons (on/off) whereas Analog preserves "
+ "floating-point magnitudes as read from controls.");
public override void OnGUI()
{
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseIMGUIEditorForAssets)) return;
#endif
target.mode = (Vector2Composite.Mode)EditorGUILayout.EnumPopup(m_ModeLabel, target.mode);
}
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
{
var modeField = new EnumField(m_ModeLabel.text, target.mode)
{
tooltip = m_ModeLabel.tooltip
};
modeField.RegisterValueChangedCallback(evt =>
{
target.mode = (Vector2Composite.Mode)evt.newValue;
onChangedCallback();
});
root.Add(modeField);
}
#endif
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4e9c0b68df9234a0bad68ad7e0391330
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,208 @@
using System;
using System.ComponentModel;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.InputSystem.Editor;
using UnityEngine.UIElements;
#endif
namespace UnityEngine.InputSystem.Composites
{
/// <summary>
/// A 3D vector formed from six floating-point inputs.
/// </summary>
/// <remarks>
/// Depending on the setting of <see cref="mode"/>, the vector is either in the [-1..1]
/// range on each axis (normalized or not depending on <see cref="mode"/>) or is in the
/// full value range of the input controls.
///
/// <example>
/// <code>
/// action.AddCompositeBinding("3DVector")
/// .With("Forward", "&lt;Keyboard&gt;/w")
/// .With("Backward", "&lt;Keyboard&gt;/s")
/// .With("Left", "&lt;Keyboard&gt;/a")
/// .With("Right", "&lt;Keyboard&gt;/d")
/// .With("Up", "&lt;Keyboard&gt;/q")
/// .With("Down", "&lt;Keyboard&gt;/e");
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="Vector2Composite"/>
[DisplayStringFormat("{up}+{down}/{left}+{right}/{forward}+{backward}")]
[DisplayName("Up/Down/Left/Right/Forward/Backward Composite")]
public class Vector3Composite : InputBindingComposite<Vector3>
{
/// <summary>
/// Binding for the button that represents the up (that is, <c>(0,1,0)</c>) direction of the vector.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int up;
/// <summary>
/// Binding for the button that represents the down (that is, <c>(0,-1,0)</c>) direction of the vector.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int down;
/// <summary>
/// Binding for the button that represents the left (that is, <c>(-1,0,0)</c>) direction of the vector.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int left;
/// <summary>
/// Binding for the button that represents the right (that is, <c>(1,0,0)</c>) direction of the vector.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int right;
/// <summary>
/// Binding for the button that represents the right (that is, <c>(0,0,1)</c>) direction of the vector.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int forward;
/// <summary>
/// Binding for the button that represents the right (that is, <c>(0,0,-1)</c>) direction of the vector.
/// </summary>
/// <remarks>
/// This property is automatically assigned by the input system.
/// </remarks>
// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once FieldCanBeMadeReadOnly.Global
[InputControl(layout = "Axis")] public int backward;
/// <summary>
/// How to synthesize a <c>Vector3</c> from the values read from <see cref="up"/>, <see cref="down"/>,
/// <see cref="left"/>, <see cref="right"/>, <see cref="forward"/>, and <see cref="backward"/>.
/// </summary>
/// <value>Determines how X, Y, and Z of the resulting <c>Vector3</c> are formed from input values.</value>
public Mode mode = Mode.Analog;
/// <inheritdoc/>
public override Vector3 ReadValue(ref InputBindingCompositeContext context)
{
if (mode == Mode.Analog)
{
var upValue = context.ReadValue<float>(up);
var downValue = context.ReadValue<float>(down);
var leftValue = context.ReadValue<float>(left);
var rightValue = context.ReadValue<float>(right);
var forwardValue = context.ReadValue<float>(forward);
var backwardValue = context.ReadValue<float>(backward);
return new Vector3(rightValue - leftValue, upValue - downValue, forwardValue - backwardValue);
}
else
{
var upValue = context.ReadValueAsButton(up) ? 1f : 0f;
var downValue = context.ReadValueAsButton(down) ? -1f : 0f;
var leftValue = context.ReadValueAsButton(left) ? -1f : 0f;
var rightValue = context.ReadValueAsButton(right) ? 1f : 0f;
var forwardValue = context.ReadValueAsButton(forward) ? 1f : 0f;
var backwardValue = context.ReadValueAsButton(backward) ? -1f : 0f;
var vector = new Vector3(leftValue + rightValue, upValue + downValue, forwardValue + backwardValue);
if (mode == Mode.DigitalNormalized)
vector = vector.normalized;
return vector;
}
}
/// <inheritdoc />
public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
{
var value = ReadValue(ref context);
return value.magnitude;
}
/// <summary>
/// Determines how a <c>Vector3</c> is synthesized from part controls.
/// </summary>
public enum Mode
{
/// <summary>
/// Part controls are treated as analog meaning that the floating-point values read from controls
/// will come through as is (minus the fact that the down and left direction values are negated).
/// </summary>
Analog,
/// <summary>
/// Part controls are treated as buttons (on/off) and the resulting vector is normalized. This means
/// that if, for example, both left and up are pressed, instead of returning a vector (-1,1,0), a vector
/// of roughly (-0.7,0.7,0) (that is, corresponding to <c>new Vector3(-1,1,0).normalized</c>) is returned instead.
/// </summary>
DigitalNormalized,
/// <summary>
/// Part controls are treated as buttons (on/off) and the resulting vector is not normalized. This means
/// that if both left and up are pressed, for example, the resulting vector is (-1,1,0) and has a length
/// greater than 1.
/// </summary>
Digital,
}
}
#if UNITY_EDITOR
internal class Vector3CompositeEditor : InputParameterEditor<Vector3Composite>
{
private GUIContent m_ModeLabel = new GUIContent("Mode",
"How to synthesize a Vector3 from the inputs. Digital "
+ "treats part bindings as buttons (on/off) whereas Analog preserves "
+ "floating-point magnitudes as read from controls.");
public override void OnGUI()
{
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseIMGUIEditorForAssets)) return;
#endif
target.mode = (Vector3Composite.Mode)EditorGUILayout.EnumPopup(m_ModeLabel, target.mode);
}
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
{
var modeField = new EnumField(m_ModeLabel.text, target.mode)
{
tooltip = m_ModeLabel.tooltip
};
modeField.RegisterValueChangedCallback(evt =>
{
target.mode = (Vector3Composite.Mode)evt.newValue;
onChangedCallback();
});
root.Add(modeField);
}
#endif
}
#endif
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cda1b45d3bda468a95dca208b99174da
timeCreated: 1597842391