File size: 11,440 Bytes
18a519f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | 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()
{
EditorGUILayout.HelpBox(s_HelpBoxText);
target.behavior = (PressBehavior)EditorGUILayout.EnumPopup(s_PressBehaviorLabel, target.behavior);
m_PressPointSetting.OnGUI();
}
#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR
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);
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
}
|