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

View File

@@ -0,0 +1,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: "&lt;Gamepad&gt;/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
}

View File

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

View File

@@ -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
}

View File

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

View File

@@ -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
}

View File

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

View File

@@ -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
}

View File

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

View File

@@ -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
}

View File

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