test
This commit is contained in:
@@ -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", "<Keyboard>/a")
|
||||
/// .With("Positive", "<Keyboard>/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", "<Keyboard>/a")
|
||||
/// .With("Positive", "<Keyboard>/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", "<Keyboard>/a")
|
||||
/// .With("Positive", "<Keyboard>/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
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61fdc882d66f0f34d90450c001c0078e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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", "<Keyboard>/leftCtrl")
|
||||
/// .With("Modifier", "<Keyboard>/rightControl")
|
||||
/// .With("Button", "<Keyboard>/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", "<Gamepad>/buttonSouth")
|
||||
/// .With("Button", "<Gamepad>/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><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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15164829aab964eedaee6bac785c2c05
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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", "<Keyboard>/leftCtrl")
|
||||
/// .With("Modifier1", "<Keyboard>/rightCtrl")
|
||||
/// .With("Modifier2", "<Keyboard>/leftShift")
|
||||
/// .With("Modifier2", "<Keyboard>/rightShift")
|
||||
/// .With("Button", "<Keyboard>/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", "<Gamepad>/buttonSouth")
|
||||
/// .With("Modifier2", "<Gamepad>/buttonWest")
|
||||
/// .With("Button", "<Gamepad>/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><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><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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62dcc18c42c2246bdaed7fe210b77118
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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", "<Keyboard>/ctrl")
|
||||
/// .With("Binding", "<Keyboard>/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", "<Keyboard>/alt")
|
||||
/// .With("Binding", "<Mouse>/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><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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a0a9c8a3d9c4893ab5389e009563314
|
||||
timeCreated: 1588685554
|
@@ -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", "<Keyboard>/ctrl")
|
||||
/// .With("Modifier", "<Keyboard>/shift")
|
||||
/// .With("Binding", "<Keyboard>/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", "<Keyboard>/ctrl")
|
||||
/// .With("Modifier2", "<Keyboard>/shift")
|
||||
/// .With("Binding", "<Mouse>/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><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><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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be272fdf97df4e8daf1393dabdfa4cb8
|
||||
timeCreated: 1588685577
|
@@ -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", "<Keyboard>/w")
|
||||
/// .With("Left", "<Keyboard>/a")
|
||||
/// .With("Down", "<Keyboard>/s")
|
||||
/// .With("Right", "<Keyboard>/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", "<Gamepad>/leftStick/up")
|
||||
/// .With("down", "<Gamepad>/leftStick/down")
|
||||
/// .With("left", "<Gamepad>/leftStick/left")
|
||||
/// .With("right", "<Gamepad>/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", "<Gamepad>/leftStick/up")
|
||||
/// .With("down", "<Gamepad>/leftStick/down")
|
||||
/// .With("left", "<Gamepad>/leftStick/left")
|
||||
/// .With("right", "<Gamepad>/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", "<Gamepad>/leftStick/up")
|
||||
/// .With("down", "<Gamepad>/leftStick/down")
|
||||
/// .With("left", "<Gamepad>/leftStick/left")
|
||||
/// .With("right", "<Gamepad>/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
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e9c0b68df9234a0bad68ad7e0391330
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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", "<Keyboard>/w")
|
||||
/// .With("Backward", "<Keyboard>/s")
|
||||
/// .With("Left", "<Keyboard>/a")
|
||||
/// .With("Right", "<Keyboard>/d")
|
||||
/// .With("Up", "<Keyboard>/q")
|
||||
/// .With("Down", "<Keyboard>/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
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cda1b45d3bda468a95dca208b99174da
|
||||
timeCreated: 1597842391
|
Reference in New Issue
Block a user