test
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.Scripting;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine.InputSystem.Editor;
|
||||
using UnityEngine.UIElements;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.InputSystem.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs the action if the control is pressed and held for at least the
|
||||
/// set duration (which defaults to <see cref="InputSettings.defaultHoldTime"/>).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The action is started when the control is pressed. If the control is released before the
|
||||
/// set <see cref="duration"/>, the action is canceled. As soon as the hold time is reached,
|
||||
/// the action performs. The action then stays performed until the control is released, at
|
||||
/// which point the action cancels.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Action that requires A button on gamepad to be held for half a second.
|
||||
/// var action = new InputAction(binding: "<Gamepad>/buttonSouth", interactions: "hold(duration=0.5)");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
[DisplayName("Hold")]
|
||||
public class HoldInteraction : IInputInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Duration in seconds that the control must be pressed for the hold to register.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this is less than or equal to 0 (the default), <see cref="InputSettings.defaultHoldTime"/> is used.
|
||||
///
|
||||
/// Duration is expressed in real time and measured against the timestamps of input events
|
||||
/// (<see cref="LowLevel.InputEvent.time"/>) not against game time (<see cref="Time.time"/>).
|
||||
/// </remarks>
|
||||
public float duration;
|
||||
|
||||
/// <summary>
|
||||
/// Magnitude threshold that must be crossed by an actuated control for the control to
|
||||
/// be considered pressed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this is less than or equal to 0 (the default), <see cref="InputSettings.defaultButtonPressPoint"/> is used instead.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControl.EvaluateMagnitude()"/>
|
||||
public float pressPoint;
|
||||
|
||||
private float durationOrDefault => duration > 0.0 ? duration : InputSystem.settings.defaultHoldTime;
|
||||
private float pressPointOrDefault => pressPoint > 0.0 ? pressPoint : ButtonControl.s_GlobalDefaultButtonPressPoint;
|
||||
|
||||
private double m_TimePressed;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Process(ref InputInteractionContext context)
|
||||
{
|
||||
if (context.timerHasExpired)
|
||||
{
|
||||
context.PerformedAndStayPerformed();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (context.phase)
|
||||
{
|
||||
case InputActionPhase.Waiting:
|
||||
if (context.ControlIsActuated(pressPointOrDefault))
|
||||
{
|
||||
m_TimePressed = context.time;
|
||||
|
||||
context.Started();
|
||||
context.SetTimeout(durationOrDefault);
|
||||
}
|
||||
break;
|
||||
|
||||
case InputActionPhase.Started:
|
||||
// If we've reached our hold time threshold, perform the hold.
|
||||
// We do this regardless of what state the control changed to.
|
||||
if (context.time - m_TimePressed >= durationOrDefault)
|
||||
{
|
||||
context.PerformedAndStayPerformed();
|
||||
}
|
||||
if (!context.ControlIsActuated())
|
||||
{
|
||||
// Control is no longer actuated so we're done.
|
||||
context.Canceled();
|
||||
}
|
||||
break;
|
||||
|
||||
case InputActionPhase.Performed:
|
||||
if (!context.ControlIsActuated(pressPointOrDefault))
|
||||
context.Canceled();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Reset()
|
||||
{
|
||||
m_TimePressed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// UI that is displayed when editing <see cref="HoldInteraction"/> in the editor.
|
||||
/// </summary>
|
||||
internal class HoldInteractionEditor : InputParameterEditor<HoldInteraction>
|
||||
{
|
||||
protected override void OnEnable()
|
||||
{
|
||||
m_PressPointSetting.Initialize("Press Point",
|
||||
"Float value that an axis control has to cross for it to be considered pressed.",
|
||||
"Default Button Press Point",
|
||||
() => target.pressPoint, v => target.pressPoint = v, () => ButtonControl.s_GlobalDefaultButtonPressPoint);
|
||||
m_DurationSetting.Initialize("Hold Time",
|
||||
"Time (in seconds) that a control has to be held in order for it to register as a hold.",
|
||||
"Default Hold Time",
|
||||
() => target.duration, x => target.duration = x, () => InputSystem.settings.defaultHoldTime);
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseIMGUIEditorForAssets)) return;
|
||||
#endif
|
||||
m_PressPointSetting.OnGUI();
|
||||
m_DurationSetting.OnGUI();
|
||||
}
|
||||
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
|
||||
{
|
||||
m_PressPointSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
m_DurationSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private CustomOrDefaultSetting m_PressPointSetting;
|
||||
private CustomOrDefaultSetting m_DurationSetting;
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bd8ebd167bb9d34cabf0b443f2d1aaa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,237 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.Scripting;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine.InputSystem.Editor;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
#endif
|
||||
|
||||
////TODO: add ability to respond to any of the taps in the sequence (e.g. one response for single tap, another for double tap)
|
||||
|
||||
////TODO: add ability to perform on final press rather than on release
|
||||
|
||||
////TODO: change this so that the interaction stays performed when the tap count is reached until the button is released
|
||||
|
||||
namespace UnityEngine.InputSystem.Interactions
|
||||
{
|
||||
////REVIEW: Why is this deriving from IInputInteraction<float>? It goes by actuation just like Hold etc.
|
||||
/// <summary>
|
||||
/// Interaction that requires multiple taps (press and release within <see cref="tapTime"/>) spaced no more
|
||||
/// than <see cref="tapDelay"/> seconds apart. This equates to a chain of <see cref="TapInteraction"/> with
|
||||
/// a maximum delay between each tap.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The interaction goes into <see cref="InputActionPhase.Started"/> on the first press and then will not
|
||||
/// trigger again until either the full tap sequence is performed (in which case the interaction triggers
|
||||
/// <see cref="InputActionPhase.Performed"/>) or the multi-tap is aborted by a timeout being hit (in which
|
||||
/// case the interaction will trigger <see cref="InputActionPhase.Canceled"/>).
|
||||
/// </remarks>
|
||||
public class MultiTapInteraction : IInputInteraction<float>
|
||||
{
|
||||
/// <summary>
|
||||
/// The time in seconds within which the control needs to be pressed and released to perform the interaction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this value is equal to or smaller than zero, the input system will use (<see cref="InputSettings.defaultTapTime"/>) instead.
|
||||
/// </remarks>
|
||||
[Tooltip("The maximum time (in seconds) allowed to elapse between pressing and releasing a control for it to register as a tap.")]
|
||||
public float tapTime;
|
||||
|
||||
/// <summary>
|
||||
/// The time in seconds which is allowed to pass between taps.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this time is exceeded, the multi-tap interaction is canceled.
|
||||
/// If this value is equal to or smaller than zero, the input system will use the duplicate value of <see cref="tapTime"/> instead.
|
||||
/// </remarks>
|
||||
[Tooltip("The maximum delay (in seconds) allowed between each tap. If this time is exceeded, the multi-tap is canceled.")]
|
||||
public float tapDelay;
|
||||
|
||||
/// <summary>
|
||||
/// The number of taps required to perform the interaction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// How many taps need to be performed in succession. Two means double-tap, three means triple-tap, and so on.
|
||||
/// </remarks>
|
||||
[Tooltip("How many taps need to be performed in succession. Two means double-tap, three means triple-tap, and so on.")]
|
||||
public int tapCount = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Magnitude threshold that must be crossed by an actuated control for the control to
|
||||
/// be considered pressed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this is less than or equal to 0 (the default), <see cref="InputSettings.defaultButtonPressPoint"/> is used instead.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControl.EvaluateMagnitude()"/>
|
||||
public float pressPoint;
|
||||
|
||||
private float tapTimeOrDefault => tapTime > 0.0 ? tapTime : InputSystem.settings.defaultTapTime;
|
||||
internal float tapDelayOrDefault => tapDelay > 0.0 ? tapDelay : InputSystem.settings.multiTapDelayTime;
|
||||
private float pressPointOrDefault => pressPoint > 0 ? pressPoint : ButtonControl.s_GlobalDefaultButtonPressPoint;
|
||||
private float releasePointOrDefault => pressPointOrDefault * ButtonControl.s_GlobalDefaultButtonReleaseThreshold;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Process(ref InputInteractionContext context)
|
||||
{
|
||||
if (context.timerHasExpired)
|
||||
{
|
||||
// We use timers multiple times but no matter what, if they expire it means
|
||||
// that we didn't get input in time.
|
||||
context.Canceled();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_CurrentTapPhase)
|
||||
{
|
||||
case TapPhase.None:
|
||||
if (context.ControlIsActuated(pressPointOrDefault))
|
||||
{
|
||||
m_CurrentTapPhase = TapPhase.WaitingForNextRelease;
|
||||
m_CurrentTapStartTime = context.time;
|
||||
context.Started();
|
||||
|
||||
var maxTapTime = tapTimeOrDefault;
|
||||
var maxDelayInBetween = tapDelayOrDefault;
|
||||
context.SetTimeout(maxTapTime);
|
||||
|
||||
// We'll be using multiple timeouts so set a total completion time that
|
||||
// effects the result of InputAction.GetTimeoutCompletionPercentage()
|
||||
// such that it accounts for the total time we allocate for the interaction
|
||||
// rather than only the time of one single timeout.
|
||||
context.SetTotalTimeoutCompletionTime(maxTapTime * tapCount + (tapCount - 1) * maxDelayInBetween);
|
||||
}
|
||||
break;
|
||||
|
||||
case TapPhase.WaitingForNextRelease:
|
||||
if (!context.ControlIsActuated(releasePointOrDefault))
|
||||
{
|
||||
if (context.time - m_CurrentTapStartTime <= tapTimeOrDefault)
|
||||
{
|
||||
++m_CurrentTapCount;
|
||||
if (m_CurrentTapCount >= tapCount)
|
||||
{
|
||||
context.Performed();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CurrentTapPhase = TapPhase.WaitingForNextPress;
|
||||
m_LastTapReleaseTime = context.time;
|
||||
context.SetTimeout(tapDelayOrDefault);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Canceled();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TapPhase.WaitingForNextPress:
|
||||
if (context.ControlIsActuated(pressPointOrDefault))
|
||||
{
|
||||
if (context.time - m_LastTapReleaseTime <= tapDelayOrDefault)
|
||||
{
|
||||
m_CurrentTapPhase = TapPhase.WaitingForNextRelease;
|
||||
m_CurrentTapStartTime = context.time;
|
||||
context.SetTimeout(tapTimeOrDefault);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Canceled();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Reset()
|
||||
{
|
||||
m_CurrentTapPhase = TapPhase.None;
|
||||
m_CurrentTapCount = 0;
|
||||
m_CurrentTapStartTime = 0;
|
||||
m_LastTapReleaseTime = 0;
|
||||
}
|
||||
|
||||
private TapPhase m_CurrentTapPhase;
|
||||
private int m_CurrentTapCount;
|
||||
private double m_CurrentTapStartTime;
|
||||
private double m_LastTapReleaseTime;
|
||||
|
||||
private enum TapPhase
|
||||
{
|
||||
None,
|
||||
WaitingForNextRelease,
|
||||
WaitingForNextPress,
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// UI that is displayed when editing <see cref="HoldInteraction"/> in the editor.
|
||||
/// </summary>
|
||||
internal class MultiTapInteractionEditor : InputParameterEditor<MultiTapInteraction>
|
||||
{
|
||||
protected override void OnEnable()
|
||||
{
|
||||
m_TapTimeSetting.Initialize("Max Tap Duration",
|
||||
"Time (in seconds) within with a control has to be released again for it to register as a tap. If the control is held "
|
||||
+ "for longer than this time, the tap is canceled.",
|
||||
"Default Tap Time",
|
||||
() => target.tapTime, x => target.tapTime = x, () => InputSystem.settings.defaultTapTime);
|
||||
m_TapDelaySetting.Initialize("Max Tap Spacing",
|
||||
"The maximum delay (in seconds) allowed between each tap. If this time is exceeded, the multi-tap is canceled.",
|
||||
"Default Tap Spacing",
|
||||
() => target.tapDelay, x => target.tapDelay = x, () => InputSystem.settings.multiTapDelayTime);
|
||||
m_PressPointSetting.Initialize("Press Point",
|
||||
"The amount of actuation a control requires before being considered pressed. If not set, default to "
|
||||
+ "'Default Button Press Point' in the global input settings.",
|
||||
"Default Button Press Point",
|
||||
() => target.pressPoint, v => target.pressPoint = v,
|
||||
() => InputSystem.settings.defaultButtonPressPoint);
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseIMGUIEditorForAssets)) return;
|
||||
#endif
|
||||
target.tapCount = EditorGUILayout.IntField(m_TapCountLabel, target.tapCount);
|
||||
m_TapDelaySetting.OnGUI();
|
||||
m_TapTimeSetting.OnGUI();
|
||||
m_PressPointSetting.OnGUI();
|
||||
}
|
||||
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
|
||||
{
|
||||
var tapCountField = new IntegerField(m_TapCountLabel.text)
|
||||
{
|
||||
value = target.tapCount,
|
||||
tooltip = m_TapCountLabel.tooltip
|
||||
};
|
||||
tapCountField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
target.tapCount = evt.newValue;
|
||||
onChangedCallback?.Invoke();
|
||||
});
|
||||
root.Add(tapCountField);
|
||||
|
||||
m_TapDelaySetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
m_TapTimeSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
m_PressPointSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private readonly GUIContent m_TapCountLabel = new GUIContent("Tap Count", "How many taps need to be performed in succession. Two means double-tap, three means triple-tap, and so on.");
|
||||
|
||||
private CustomOrDefaultSetting m_PressPointSetting;
|
||||
private CustomOrDefaultSetting m_TapTimeSetting;
|
||||
private CustomOrDefaultSetting m_TapDelaySetting;
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2732a66825034f739cb072c49aa8990
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,257 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.Scripting;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine.InputSystem.Editor;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
#endif
|
||||
|
||||
////TODO: protect against the control *hovering* around the press point; this should not fire the press repeatedly; probably need a zone around the press point
|
||||
////TODO: also, for analog controls, we probably want a deadzone that gives just a tiny little buffer at the low end before the action starts
|
||||
|
||||
////REVIEW: shouldn't it use Canceled for release on PressAndRelease instead of triggering Performed again?
|
||||
|
||||
namespace UnityEngine.InputSystem.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs the action at specific points in a button press-and-release sequence according top <see cref="behavior"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By default, uses <see cref="PressBehavior.PressOnly"/> which performs the action as soon as the control crosses the
|
||||
/// button press threshold defined by <see cref="pressPoint"/>. The action then will not trigger again until the control
|
||||
/// is first released.
|
||||
///
|
||||
/// Can be set to instead trigger on release (that is, when the control goes back below the button press threshold) using
|
||||
/// <see cref="PressBehavior.ReleaseOnly"/> or can be set to trigger on both press and release using <see cref="PressBehavior.PressAndRelease"/>).
|
||||
///
|
||||
/// Note that using an explicit press interaction is only necessary if the goal is to either customize the press behavior
|
||||
/// of a button or when binding to controls that are not buttons as such (the press interaction compares magnitudes to
|
||||
/// <see cref="pressPoint"/> and thus any type of control that can deliver a magnitude can act as a button). The default
|
||||
/// behavior available out of the box when binding <see cref="InputActionType.Button"/> type actions to button-type controls
|
||||
/// (<see cref="UnityEngine.InputSystem.Controls.ButtonControl"/>) corresponds to using a press modifier with <see cref="behavior"/>
|
||||
/// set to <see cref="PressBehavior.PressOnly"/> and <see cref="pressPoint"/> left at default.
|
||||
/// </remarks>
|
||||
[DisplayName("Press")]
|
||||
public class PressInteraction : IInputInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Amount of actuation required before a control is considered pressed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If zero (default), defaults to <see cref="InputSettings.defaultButtonPressPoint"/>.
|
||||
/// </remarks>
|
||||
[Tooltip("The amount of actuation a control requires before being considered pressed. If not set, default to "
|
||||
+ "'Default Press Point' in the global input settings.")]
|
||||
public float pressPoint;
|
||||
|
||||
////REVIEW: this should really be named "pressBehavior"
|
||||
/// <summary>
|
||||
/// Determines how button presses trigger the action.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By default (PressOnly), the action is performed on press.
|
||||
/// With ReleaseOnly, the action is performed on release. With PressAndRelease, the action is
|
||||
/// performed on press and on release.
|
||||
/// </remarks>
|
||||
[Tooltip("Determines how button presses trigger the action. By default (PressOnly), the action is performed on press. "
|
||||
+ "With ReleaseOnly, the action is performed on release. With PressAndRelease, the action is performed on press and release.")]
|
||||
public PressBehavior behavior;
|
||||
|
||||
private float pressPointOrDefault => pressPoint > 0 ? pressPoint : ButtonControl.s_GlobalDefaultButtonPressPoint;
|
||||
private float releasePointOrDefault => pressPointOrDefault * ButtonControl.s_GlobalDefaultButtonReleaseThreshold;
|
||||
private bool m_WaitingForRelease;
|
||||
|
||||
public void Process(ref InputInteractionContext context)
|
||||
{
|
||||
var actuation = context.ComputeMagnitude();
|
||||
switch (behavior)
|
||||
{
|
||||
case PressBehavior.PressOnly:
|
||||
if (m_WaitingForRelease)
|
||||
{
|
||||
if (actuation <= releasePointOrDefault)
|
||||
{
|
||||
m_WaitingForRelease = false;
|
||||
if (Mathf.Approximately(0f, actuation))
|
||||
context.Canceled();
|
||||
else
|
||||
context.Started();
|
||||
}
|
||||
}
|
||||
else if (actuation >= pressPointOrDefault)
|
||||
{
|
||||
m_WaitingForRelease = true;
|
||||
// Stay performed until release.
|
||||
context.PerformedAndStayPerformed();
|
||||
}
|
||||
else if (actuation > 0 && !context.isStarted)
|
||||
{
|
||||
context.Started();
|
||||
}
|
||||
else if (Mathf.Approximately(0f, actuation) && context.isStarted)
|
||||
{
|
||||
context.Canceled();
|
||||
}
|
||||
break;
|
||||
|
||||
case PressBehavior.ReleaseOnly:
|
||||
if (m_WaitingForRelease)
|
||||
{
|
||||
if (actuation <= releasePointOrDefault)
|
||||
{
|
||||
m_WaitingForRelease = false;
|
||||
context.Performed();
|
||||
context.Canceled();
|
||||
}
|
||||
}
|
||||
else if (actuation >= pressPointOrDefault)
|
||||
{
|
||||
m_WaitingForRelease = true;
|
||||
if (!context.isStarted)
|
||||
context.Started();
|
||||
}
|
||||
else
|
||||
{
|
||||
var started = context.isStarted;
|
||||
if (actuation > 0 && !started)
|
||||
context.Started();
|
||||
else if (Mathf.Approximately(0, actuation) && started)
|
||||
context.Canceled();
|
||||
}
|
||||
break;
|
||||
|
||||
case PressBehavior.PressAndRelease:
|
||||
if (m_WaitingForRelease)
|
||||
{
|
||||
if (actuation <= releasePointOrDefault)
|
||||
{
|
||||
m_WaitingForRelease = false;
|
||||
context.Performed();
|
||||
if (Mathf.Approximately(0, actuation))
|
||||
context.Canceled();
|
||||
}
|
||||
}
|
||||
else if (actuation >= pressPointOrDefault)
|
||||
{
|
||||
m_WaitingForRelease = true;
|
||||
context.PerformedAndStayPerformed();
|
||||
}
|
||||
else
|
||||
{
|
||||
var started = context.isStarted;
|
||||
if (actuation > 0 && !started)
|
||||
context.Started();
|
||||
else if (Mathf.Approximately(0, actuation) && started)
|
||||
context.Canceled();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
m_WaitingForRelease = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines how to trigger an action based on button presses.
|
||||
/// </summary>
|
||||
/// <seealso cref="PressInteraction.behavior"/>
|
||||
public enum PressBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Perform the action when the button is pressed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggers <see cref="InputAction.performed"/> when a control crosses the button press threshold.
|
||||
/// </remarks>
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
PressOnly = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Perform the action when the button is released.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggers <see cref="InputAction.started"/> when a control crosses the button press threshold and
|
||||
/// <see cref="InputAction.performed"/> when the control goes back below the button press threshold.
|
||||
/// </remarks>
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
ReleaseOnly = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Perform the action when the button is pressed and when the button is released.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggers <see cref="InputAction.performed"/> when a control crosses the button press threshold
|
||||
/// and triggers <see cref="InputAction.performed"/> again when it goes back below the button press
|
||||
/// threshold.
|
||||
/// </remarks>
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
PressAndRelease = 2,
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// UI that is displayed when editing <see cref="PressInteraction"/> in the editor.
|
||||
/// </summary>
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
internal class PressInteractionEditor : InputParameterEditor<PressInteraction>
|
||||
{
|
||||
protected override void OnEnable()
|
||||
{
|
||||
m_PressPointSetting.Initialize("Press Point",
|
||||
"The amount of actuation a control requires before being considered pressed. If not set, default to "
|
||||
+ "'Default Button Press Point' in the global input settings.",
|
||||
"Default Button Press Point",
|
||||
() => target.pressPoint, v => target.pressPoint = v,
|
||||
() => InputSystem.settings.defaultButtonPressPoint);
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseIMGUIEditorForAssets)) return;
|
||||
#endif
|
||||
EditorGUILayout.HelpBox(s_HelpBoxText);
|
||||
target.behavior = (PressBehavior)EditorGUILayout.EnumPopup(s_PressBehaviorLabel, target.behavior);
|
||||
m_PressPointSetting.OnGUI();
|
||||
}
|
||||
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
|
||||
{
|
||||
root.Add(new HelpBox(s_HelpBoxText.text, HelpBoxMessageType.None));
|
||||
|
||||
var behaviourDropdown = new EnumField(s_PressBehaviorLabel.text, target.behavior)
|
||||
{
|
||||
tooltip = s_PressBehaviorLabel.tooltip
|
||||
};
|
||||
behaviourDropdown.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
target.behavior = (PressBehavior)evt.newValue;
|
||||
onChangedCallback?.Invoke();
|
||||
});
|
||||
root.Add(behaviourDropdown);
|
||||
|
||||
m_PressPointSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private CustomOrDefaultSetting m_PressPointSetting;
|
||||
|
||||
private static readonly GUIContent s_HelpBoxText = EditorGUIUtility.TrTextContent("Note that the 'Press' interaction is only "
|
||||
+ "necessary when wanting to customize button press behavior. For default press behavior, simply set the action type to 'Button' "
|
||||
+ "and use the action without interactions added to it.");
|
||||
|
||||
private static readonly GUIContent s_PressBehaviorLabel = EditorGUIUtility.TrTextContent("Trigger Behavior",
|
||||
"Determines how button presses trigger the action. By default (PressOnly), the action is performed on press. "
|
||||
+ "With ReleaseOnly, the action is performed on release. With PressAndRelease, the action is performed on press and "
|
||||
+ "canceled on release.");
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 802caa2f96f49470facf48de1e652f16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.Scripting;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine.InputSystem.Editor;
|
||||
using UnityEngine.UIElements;
|
||||
#endif
|
||||
|
||||
////REVIEW: this is confusing when considered next to HoldInteraction; also it's confusingly named
|
||||
|
||||
namespace UnityEngine.InputSystem.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs the action if the control is pressed and held for at least the set
|
||||
/// duration (which defaults to <see cref="InputSettings.defaultSlowTapTime"/>)
|
||||
/// and then released.
|
||||
/// </summary>
|
||||
[DisplayName("Long Tap")]
|
||||
public class SlowTapInteraction : IInputInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// The time in seconds within which the control needs to be pressed and released to perform the interaction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this value is equal to or smaller than zero, the input system will use (<see cref="InputSettings.defaultSlowTapTime"/>) instead.
|
||||
/// </remarks>
|
||||
public float duration;
|
||||
|
||||
/// <summary>
|
||||
/// The press point required to perform the interaction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For analog controls (such as trigger axes on a gamepad), the control needs to be engaged by at least this
|
||||
/// value to perform the interaction.
|
||||
/// If this value is equal to or smaller than zero, the input system will use (<see cref="InputSettings.defaultButtonPressPoint"/>) instead.
|
||||
/// </remarks>
|
||||
public float pressPoint;
|
||||
|
||||
////REVIEW: this seems stupid; shouldn't a slow tap just be anything that takes longer than TapTime?
|
||||
private float durationOrDefault => duration > 0.0f ? duration : InputSystem.settings.defaultSlowTapTime;
|
||||
private float pressPointOrDefault => pressPoint > 0 ? pressPoint : ButtonControl.s_GlobalDefaultButtonPressPoint;
|
||||
|
||||
private double m_SlowTapStartTime;
|
||||
|
||||
public void Process(ref InputInteractionContext context)
|
||||
{
|
||||
if (context.isWaiting && context.ControlIsActuated(pressPointOrDefault))
|
||||
{
|
||||
m_SlowTapStartTime = context.time;
|
||||
context.Started();
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.isStarted && !context.ControlIsActuated(pressPointOrDefault))
|
||||
{
|
||||
if (context.time - m_SlowTapStartTime >= durationOrDefault)
|
||||
context.Performed();
|
||||
else
|
||||
////REVIEW: does it matter to cancel right after expiration of 'duration' or is it enough to cancel on button up like here?
|
||||
context.Canceled();
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
m_SlowTapStartTime = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal class SlowTapInteractionEditor : InputParameterEditor<SlowTapInteraction>
|
||||
{
|
||||
protected override void OnEnable()
|
||||
{
|
||||
m_DurationSetting.Initialize("Min Tap Duration",
|
||||
"Minimum time (in seconds) that a control has to be held for it to register as a slow tap. If the control is released "
|
||||
+ "before this time, the slow tap is canceled.",
|
||||
"Default Slow Tap Time",
|
||||
() => target.duration, x => target.duration = x, () => InputSystem.settings.defaultSlowTapTime);
|
||||
m_PressPointSetting.Initialize("Press Point",
|
||||
"The amount of actuation a control requires before being considered pressed. If not set, default to "
|
||||
+ "'Default Button Press Point' in the global input settings.",
|
||||
"Default Button Press Point",
|
||||
() => target.pressPoint, v => target.pressPoint = v,
|
||||
() => InputSystem.settings.defaultButtonPressPoint);
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseIMGUIEditorForAssets)) return;
|
||||
#endif
|
||||
m_DurationSetting.OnGUI();
|
||||
m_PressPointSetting.OnGUI();
|
||||
}
|
||||
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
|
||||
{
|
||||
m_DurationSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
m_PressPointSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private CustomOrDefaultSetting m_DurationSetting;
|
||||
private CustomOrDefaultSetting m_PressPointSetting;
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3afbef7bebf197d43b2352b971a4e15f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.Scripting;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine.InputSystem.Editor;
|
||||
using UnityEngine.UIElements;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.InputSystem.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs the action if the control is pressed held for at least the set
|
||||
/// duration (which defaults to <see cref="InputSettings.defaultTapTime"/>)
|
||||
/// and then released.
|
||||
/// </summary>
|
||||
[DisplayName("Tap")]
|
||||
public class TapInteraction : IInputInteraction
|
||||
{
|
||||
////REVIEW: this should be called tapTime
|
||||
/// <summary>
|
||||
/// The time in seconds within which the control needs to be pressed and released to perform the interaction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this value is equal to or smaller than zero, the input system will use (<see cref="InputSettings.defaultTapTime"/>) instead.
|
||||
/// </remarks>
|
||||
public float duration;
|
||||
|
||||
/// <summary>
|
||||
/// The press point required to perform the interaction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For analog controls (such as trigger axes on a gamepad), the control needs to be engaged by at least this
|
||||
/// value to perform the interaction.
|
||||
/// If this value is equal to or smaller than zero, the input system will use (<see cref="InputSettings.defaultButtonPressPoint"/>) instead.
|
||||
/// </remarks>
|
||||
public float pressPoint;
|
||||
|
||||
private float durationOrDefault => duration > 0.0 ? duration : InputSystem.settings.defaultTapTime;
|
||||
private float pressPointOrDefault => pressPoint > 0 ? pressPoint : ButtonControl.s_GlobalDefaultButtonPressPoint;
|
||||
private float releasePointOrDefault => pressPointOrDefault * ButtonControl.s_GlobalDefaultButtonReleaseThreshold;
|
||||
|
||||
private double m_TapStartTime;
|
||||
|
||||
////TODO: make sure 2d doesn't move too far
|
||||
|
||||
public void Process(ref InputInteractionContext context)
|
||||
{
|
||||
if (context.timerHasExpired)
|
||||
{
|
||||
context.Canceled();
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.isWaiting && context.ControlIsActuated(pressPointOrDefault))
|
||||
{
|
||||
m_TapStartTime = context.time;
|
||||
// Set timeout slightly after duration so that if tap comes in exactly at the expiration
|
||||
// time, it still counts as a valid tap.
|
||||
context.Started();
|
||||
context.SetTimeout(durationOrDefault + 0.00001f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.isStarted && !context.ControlIsActuated(releasePointOrDefault))
|
||||
{
|
||||
if (context.time - m_TapStartTime <= durationOrDefault)
|
||||
{
|
||||
context.Performed();
|
||||
}
|
||||
else
|
||||
{
|
||||
////REVIEW: does it matter to cancel right after expiration of 'duration' or is it enough to cancel on button up like here?
|
||||
context.Canceled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
m_TapStartTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal class TapInteractionEditor : InputParameterEditor<TapInteraction>
|
||||
{
|
||||
protected override void OnEnable()
|
||||
{
|
||||
m_DurationSetting.Initialize("Max Tap Duration",
|
||||
"Time (in seconds) within with a control has to be released again for it to register as a tap. If the control is held "
|
||||
+ "for longer than this time, the tap is canceled.",
|
||||
"Default Tap Time",
|
||||
() => target.duration, x => target.duration = x, () => InputSystem.settings.defaultTapTime);
|
||||
m_PressPointSetting.Initialize("Press Point",
|
||||
"The amount of actuation a control requires before being considered pressed. If not set, default to "
|
||||
+ "'Default Button Press Point' in the global input settings.",
|
||||
"Default Button Press Point",
|
||||
() => target.pressPoint, v => target.pressPoint = v,
|
||||
() => InputSystem.settings.defaultButtonPressPoint);
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseIMGUIEditorForAssets)) return;
|
||||
#endif
|
||||
m_DurationSetting.OnGUI();
|
||||
m_PressPointSetting.OnGUI();
|
||||
}
|
||||
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
|
||||
{
|
||||
m_DurationSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
m_PressPointSetting.OnDrawVisualElements(root, onChangedCallback);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private CustomOrDefaultSetting m_DurationSetting;
|
||||
private CustomOrDefaultSetting m_PressPointSetting;
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: caa587f0fb5099040bf6193f93daec5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user