A game about forced loneliness, made by TACStudios
1using System;
2using Unity.Collections.LowLevel.Unsafe;
3using UnityEngine.InputSystem.Controls;
4using UnityEngine.InputSystem.LowLevel;
5using UnityEngine.InputSystem.Utilities;
6using UnityEngine.Serialization;
7
8////TODO: add way to retrieve the binding correspond to a control
9
10////TODO: add way to retrieve the currently ongoing interaction and also add way to know how long it's been going on
11
12////FIXME: control goes back to invalid when the action ends, so there's no guarantee you can get to the control through the polling API
13
14////FIXME: Whether a control from a binding that's part of a composite appears on an action is currently not consistently enforced.
15//// If it mentions the action, it appears on the action. Otherwise it doesn't. The controls should consistently appear on the
16//// action based on what action the *composite* references.
17
18////REVIEW: Should we bring the checkboxes for actions back? We tried to "simplify" things by collapsing everything into a InputActionTypes
19//// and making the various behavior toggles implicit in that. However, my impression is that this has largely backfired by making
20//// it opaque what the choices actually entail and by giving no way out if the choices for one reason or another don't work out
21//// perfectly.
22////
23//// My impression is that at least two the following two checkboxes would make sense:
24//// 1) Initial State Check? Whether the action should immediately sync to the current state of controls when enabled.
25//// 2) Resolve Conflicting Inputs? Whether the action should try to resolve conflicts between multiple concurrent inputs.
26////
27//// I'm fine hiding this under an "Advanced" foldout or something. But IMO, control over this should be available to the user.
28////
29//// In the same vein, we probably also should expose control over how an action behaves on focus loss (https://forum.unity.com/threads/actions-canceled-when-game-loses-focus.855217/).
30
31////REVIEW: I think the action system as it is today offers too many ways to shoot yourself in the foot. It has
32//// flexibility but at the same time has abundant opportunity for ending up with dysfunction. Common setups
33//// have to come preconfigured and work robustly for the user without requiring much understanding of how
34//// the system fits together.
35
36////REVIEW: add "lastControl" property? (and maybe a lastDevice at the InputActionMap/Asset level?)
37
38////REVIEW: have single delegate instead of separate performed/started/canceled callbacks?
39
40////REVIEW: Do we need to have separate display names for actions?
41
42////REVIEW: what about having the concept of "consumed" on the callback context?
43
44////REVIEW: have "Always Enabled" toggle on actions?
45
46////TODO: allow temporarily disabling individual bindings (flag on binding) such that no re-resolve is needed
47//// (SilenceBinding? DisableBinding)
48
49namespace UnityEngine.InputSystem
50{
51 /// <summary>
52 /// A named input signal that can flexibly decide which input data to tap.
53 /// </summary>
54 /// <remarks>
55 /// An input action is an abstraction over the source of input(s) it receives. They are
56 /// most useful for representing input as "logical" concepts (e.g. "jump") rather than
57 /// as "physical" inputs (e.g. "space bar on keyboard pressed").
58 ///
59 /// In its most basic form, an action is simply an object along with a collection of
60 /// bindings that trigger the action.
61 ///
62 /// <example>
63 /// <code>
64 /// // A simple action can be created directly using `new`. If desired, a binding
65 /// // can be specified directly as part of construction.
66 /// var action = new InputAction(binding: "<Gamepad>/buttonSouth");
67 ///
68 /// // Additional bindings can be added using `AddBinding`.
69 /// action.AddBinding("<Mouse>/leftButton");
70 /// </code>
71 /// </example>
72 ///
73 /// Bindings use control path expressions to reference controls. See <see cref="InputBinding"/>
74 /// for more details. There may be arbitrary many bindings targeting a single action. The
75 /// list of bindings targeting an action can be obtained through <see cref="bindings"/>.
76 ///
77 /// By itself an action does not do anything until it is enabled:
78 ///
79 /// <example>
80 /// <code>
81 /// action.Enable();
82 /// </code>
83 /// </example>
84 ///
85 /// Once enabled, the action will actively monitor all controls on devices present
86 /// in the system (see <see cref="InputSystem.devices"/>) that match any of the binding paths
87 /// associated with the action. If you want to restrict the set of bindings used at runtime
88 /// or restrict the set of devices which controls are chosen from, you can do so using
89 /// <see cref="bindingMask"/> or, if the action is part of an <see cref="InputActionMap"/>,
90 /// by setting the <see cref="InputActionMap.devices"/> property of the action map. The
91 /// controls that an action uses can be queried using the <see cref="controls"/> property.
92 ///
93 /// When input is received on controls bound to an action, the action will trigger callbacks
94 /// in response. These callbacks are <see cref="started"/>, <see cref="performed"/>, and
95 /// <see cref="canceled"/>. The callbacks are triggered as part of input system updates
96 /// (see <see cref="InputSystem.Update"/>), i.e. they happen before the respective
97 /// <c>MonoBehaviour.Update</c> or <c>MonoBehaviour.FixedUpdate</c> methods
98 /// get executed (depending on which <see cref="InputSettings.updateMode"/> the system is
99 /// set to).
100 ///
101 /// In what order and how those callbacks get triggered depends on both the <see cref="type"/>
102 /// of the action as well as on the interactions (see <see cref="IInputInteraction"/>) present
103 /// on the bindings of the action. The default behavior is that when a control is actuated
104 /// (that is, moving away from its resting position), <see cref="started"/> is called and then
105 /// <see cref="performed"/>. Subsequently, whenever the a control further changes value to
106 /// anything other than its default value, <see cref="performed"/> will be called again.
107 /// Finally, when the control moves back to its default value (i.e. resting position),
108 /// <see cref="canceled"/> is called.
109 ///
110 /// To hook into the callbacks, there are several options available to you. The most obvious
111 /// one is to hook directly into <see cref="started"/>, <see cref="performed"/>, and/or
112 /// <see cref="canceled"/>. In these callbacks, you will receive a <see cref="CallbackContext"/>
113 /// with information about how the action got triggered. For example, you can use <see
114 /// cref="CallbackContext.ReadValue{TValue}"/> to read the value from the binding that triggered
115 /// or use <see cref="CallbackContext.interaction"/> to find the interaction that is in progress.
116 ///
117 /// <example>
118 /// <code>
119 /// action.started += context => Debug.Log($"{context.action} started");
120 /// action.performed += context => Debug.Log($"{context.action} performed");
121 /// action.canceled += context => Debug.Log($"{context.action} canceled");
122 /// </code>
123 /// </example>
124 ///
125 /// Alternatively, you can use the <see cref="InputActionMap.actionTriggered"/> callback for
126 /// actions that are part of an action map or the global <see cref="InputSystem.onActionChange"/>
127 /// callback to globally listen for action activity. To simply record action activity instead
128 /// of responding to it directly, you can use <see cref="InputActionTrace"/>.
129 ///
130 /// If you prefer to poll an action directly as part of your <c>MonoBehaviour.Update</c>
131 /// or <c>MonoBehaviour.FixedUpdate</c> logic, you can do so using the <see cref="triggered"/>
132 /// and <see cref="ReadValue{TValue}"/> methods.
133 ///
134 /// <example>
135 /// <code>
136 /// protected void Update()
137 /// {
138 /// // For a button type action.
139 /// if (action.triggered)
140 /// /* ... */;
141 ///
142 /// // For a value type action.
143 /// // (Vector2 is just an example; pick the value type that is the right
144 /// // one according to the bindings you have)
145 /// var v = action.ReadValue<Vector2>();
146 /// }
147 /// </code>
148 /// </example>
149 ///
150 /// Note that actions are not generally frame-based. What this means is that an action
151 /// will observe any value change on its connected controls, even if the control changes
152 /// value multiple times in the same frame. In practice, this means that, for example,
153 /// no button press will get missed.
154 ///
155 /// Actions can be grouped into maps (see <see cref="InputActionMap"/>) which can in turn
156 /// be grouped into assets (see <see cref="InputActionAsset"/>).
157 ///
158 /// Please note that actions are a player-only feature. They are not supported in
159 /// edit mode.
160 ///
161 /// For more in-depth reading on actions, see the <a href="../manual/Actions.html">manual</a>.
162 /// </remarks>
163 /// <seealso cref="InputActionMap"/>
164 /// <seealso cref="InputActionAsset"/>
165 /// <seealso cref="InputBinding"/>
166 [Serializable]
167 public sealed class InputAction : ICloneable, IDisposable
168 {
169 /// <summary>
170 /// Name of the action.
171 /// </summary>
172 /// <value>Plain-text name of the action.</value>
173 /// <remarks>
174 /// Can be null for anonymous actions created in code.
175 ///
176 /// If the action is part of an <see cref="InputActionMap"/>, it will have a name and the name
177 /// will be unique in the map. The name is just the name of the action alone, not a "mapName/actionName"
178 /// combination.
179 ///
180 /// The name should not contain slashes or dots but can contain spaces and other punctuation.
181 ///
182 /// An action can be renamed after creation using <see cref="InputActionSetupExtensions.Rename"/>..
183 /// </remarks>
184 /// <seealso cref="InputActionMap.FindAction(string,bool)"/>
185 public string name => m_Name;
186
187 /// <summary>
188 /// Behavior type of the action.
189 /// </summary>
190 /// <value>General behavior type of the action.</value>
191 /// <remarks>
192 /// Determines how the action gets triggered in response to control value changes.
193 ///
194 /// For details about how the action type affects an action, see <see cref="InputActionType"/>.
195 /// </remarks>
196 public InputActionType type => m_Type;
197
198 /// <summary>
199 /// A stable, unique identifier for the action.
200 /// </summary>
201 /// <value>Unique ID of the action.</value>
202 /// <remarks>
203 /// This can be used instead of the name to refer to the action. Doing so allows referring to the
204 /// action such that renaming the action does not break references.
205 /// </remarks>
206 public Guid id
207 {
208 get
209 {
210 MakeSureIdIsInPlace();
211 return new Guid(m_Id);
212 }
213 }
214
215 internal Guid idDontGenerate
216 {
217 get
218 {
219 if (string.IsNullOrEmpty(m_Id))
220 return default;
221 return new Guid(m_Id);
222 }
223 }
224
225 /// <summary>
226 /// Name of control layout expected for controls bound to this action.
227 /// </summary>
228 /// <remarks>
229 /// This is optional and is null by default.
230 ///
231 /// Constraining an action to a particular control layout allows determine the value
232 /// type and expected input behavior of an action without being reliant on any particular
233 /// binding.
234 /// </remarks>
235 public string expectedControlType
236 {
237 get => m_ExpectedControlType;
238 set => m_ExpectedControlType = value;
239 }
240
241 /// <summary>
242 /// Processors applied to every binding on the action.
243 /// </summary>
244 /// <value>Processors added to all bindings on the action.</value>
245 /// <remarks>
246 /// This property is equivalent to appending the same string to the
247 /// <see cref="InputBinding.processors"/> field of every binding that targets
248 /// the action. It is thus simply a means of avoiding the need configure the
249 /// same processor the same way on every binding in case it uniformly applies
250 /// to all of them.
251 ///
252 /// <example>
253 /// <code>
254 /// var action = new InputAction(processors: "scaleVector2(x=2, y=2)");
255 ///
256 /// // Both of the following bindings will implicitly have a
257 /// // ScaleVector2Processor applied to them.
258 /// action.AddBinding("<Gamepad>/leftStick");
259 /// action.AddBinding("<Joystick>/stick");
260 /// </code>
261 /// </example>
262 /// </remarks>
263 /// <seealso cref="InputBinding.processors"/>
264 /// <seealso cref="InputProcessor"/>
265 /// <seealso cref="InputSystem.RegisterProcessor{T}"/>
266 public string processors => m_Processors;
267
268 /// <summary>
269 /// Interactions applied to every binding on the action.
270 /// </summary>
271 /// <value>Interactions added to all bindings on the action.</value>
272 /// <remarks>
273 /// This property is equivalent to appending the same string to the
274 /// <see cref="InputBinding.interactions"/> field of every binding that targets
275 /// the action. It is thus simply a means of avoiding the need configure the
276 /// same interaction the same way on every binding in case it uniformly applies
277 /// to all of them.
278 ///
279 /// <example>
280 /// <code>
281 /// var action = new InputAction(interactions: "press");
282 ///
283 /// // Both of the following bindings will implicitly have a
284 /// // Press interaction applied to them.
285 /// action.AddBinding("<Gamepad>/buttonSouth");
286 /// action.AddBinding("<Joystick>/trigger");
287 /// </code>
288 /// </example>
289 /// </remarks>
290 /// <seealso cref="InputBinding.interactions"/>
291 /// <seealso cref="IInputInteraction"/>
292 /// <seealso cref="InputSystem.RegisterInteraction{T}"/>
293 public string interactions => m_Interactions;
294
295 /// <summary>
296 /// The map the action belongs to.
297 /// </summary>
298 /// <value><see cref="InputActionMap"/> that the action belongs to or null.</value>
299 /// <remarks>
300 /// If the action is a loose action created in code, this will be <c>null</c>.
301 ///
302 /// <example>
303 /// <code>
304 /// var action1 = new InputAction(); // action1.actionMap will be null
305 ///
306 /// var actionMap = new InputActionMap();
307 /// var action2 = actionMap.AddAction("action"); // action2.actionMap will point to actionMap
308 /// </code>
309 /// </example>
310 /// </remarks>
311 /// <seealso cref="InputActionSetupExtensions.AddAction"/>
312 public InputActionMap actionMap => isSingletonAction ? null : m_ActionMap;
313
314 /// <summary>
315 /// An optional mask that determines which bindings of the action to enable and
316 /// which to ignore.
317 /// </summary>
318 /// <value>Optional mask that determines which bindings on the action to enable.</value>
319 /// <remarks>
320 /// Binding masks can be applied at three different levels: for an entire asset through
321 /// <see cref="InputActionAsset.bindingMask"/>, for a specific map through <see
322 /// cref="InputActionMap.bindingMask"/>, and for single actions through this property.
323 /// By default, none of the masks will be set (i.e. they will be <c>null</c>).
324 ///
325 /// When an action is enabled, all the binding masks that apply to it are taken into
326 /// account. Specifically, this means that any given binding on the action will be
327 /// enabled only if it matches the mask applied to the asset, the mask applied
328 /// to the map that contains the action, and the mask applied to the action itself.
329 /// All the masks are individually optional.
330 ///
331 /// Masks are matched against bindings using <see cref="InputBinding.Matches"/>.
332 ///
333 /// Note that if you modify the masks applicable to an action while it is
334 /// enabled, the action's <see cref="controls"/> will get updated immediately to
335 /// respect the mask. To avoid repeated binding resolution, it is most efficient
336 /// to apply binding masks before enabling actions.
337 ///
338 /// Binding masks are non-destructive. All the bindings on the action are left
339 /// in place. Setting a mask will not affect the value of the <see cref="bindings"/>
340 /// property.
341 ///
342 /// <example>
343 /// <code>
344 /// // Create a free-standing action with two bindings, one in the
345 /// // "Keyboard" group and one in the "Gamepad" group.
346 /// var action = new InputAction();
347 /// action.AddBinding("<Gamepad>/buttonSouth", groups: "Gamepad");
348 /// action.AddBinding("<Keyboard>/space", groups: "Keyboard");
349 ///
350 /// // By default, all bindings will be enabled. This means if both
351 /// // a keyboard and gamepad (or several of them) is present, the action
352 /// // will respond to input from all of them.
353 /// action.Enable();
354 ///
355 /// // With a binding mask we can restrict the action to just specific
356 /// // bindings. For example, to only enable the gamepad binding:
357 /// action.bindingMask = InputBinding.MaskByGroup("Gamepad");
358 ///
359 /// // Note that we can mask by more than just by group. Masking by path
360 /// // or by action as well as a combination of these is also possible.
361 /// // We could, for example, mask for just a specific binding path:
362 /// action.bindingMask = new InputBinding()
363 /// {
364 /// // Select the keyboard binding based on its specific path.
365 /// path = "<Keyboard>/space"
366 /// };
367 /// </code>
368 /// </example>
369 /// </remarks>
370 /// <seealso cref="InputBinding.MaskByGroup"/>
371 /// <seealso cref="InputActionMap.bindingMask"/>
372 /// <seealso cref="InputActionAsset.bindingMask"/>
373 public InputBinding? bindingMask
374 {
375 get => m_BindingMask;
376 set
377 {
378 if (value == m_BindingMask)
379 return;
380
381 if (value != null)
382 {
383 var v = value.Value;
384 v.action = name;
385 value = v;
386 }
387
388 m_BindingMask = value;
389
390 var map = GetOrCreateActionMap();
391 if (map.m_State != null)
392 map.LazyResolveBindings(fullResolve: true);
393 }
394 }
395
396 /// <summary>
397 /// The list of bindings associated with the action.
398 /// </summary>
399 /// <value>List of bindings for the action.</value>
400 /// <remarks>
401 /// This list contains all bindings from <see cref="InputActionMap.bindings"/> of the action's
402 /// <see cref="actionMap"/> that reference the action through their <see cref="InputBinding.action"/>
403 /// property.
404 ///
405 /// Note that on the first call, the list may have to be extracted from the action map first which
406 /// may require allocating GC memory. However, once initialized, no further GC allocation hits should occur.
407 /// If the binding setup on the map is changed, re-initialization may be required.
408 /// </remarks>
409 /// <seealso cref="InputActionMap.bindings"/>
410 public ReadOnlyArray<InputBinding> bindings => GetOrCreateActionMap().GetBindingsForSingleAction(this);
411
412 /// <summary>
413 /// The set of controls to which the action's <see cref="bindings"/> resolve.
414 /// </summary>
415 /// <value>Controls resolved from the action's <see cref="bindings"/>.</value>
416 /// <remarks>
417 /// This property can be queried whether the action is enabled or not and will return the
418 /// set of controls that match the action's bindings according to the current setup of
419 /// binding masks (<see cref="bindingMask"/>) and device restrictions (<see
420 /// cref="InputActionMap.devices"/>).
421 ///
422 /// Note that internally, controls are not stored on a per-action basis. This means
423 /// that on the first read of this property, the list of controls for just the action
424 /// may have to be extracted which in turn may allocate GC memory. After the first read,
425 /// no further GC allocations should occur except if the set of controls is changed (e.g.
426 /// by changing the binding mask or by adding/removing devices to/from the system).
427 ///
428 /// If the property is queried when the action has not been enabled yet, the system
429 /// will first resolve controls on the action (and for all actions in the map and/or
430 /// the asset). See <a href="../manual/ActionBindings.html#binding-resolution">Binding Resolution</a>
431 /// in the manual for details.
432 ///
433 /// To map a control in this array to an index into <see cref="bindings"/>, use
434 /// <see cref="InputActionRebindingExtensions.GetBindingIndexForControl"/>.
435 ///
436 /// <example>
437 /// <code>
438 /// // Map control list to binding indices.
439 /// var bindingIndices = myAction.controls.Select(c => myAction.GetBindingIndexForControl(c));
440 /// </code>
441 /// </example>
442 ///
443 /// Note that this array will not contain the same control multiple times even if more than
444 /// one binding on an action references the same control.
445 ///
446 /// <example>
447 /// <code>
448 /// var action1 = new InputAction();
449 /// action1.AddBinding("<Gamepad>/buttonSouth");
450 /// action1.AddBinding("<Gamepad>/buttonSouth"); // This binding will be ignored.
451 ///
452 /// // Contains only one instance of buttonSouth which is associated
453 /// // with the first binding (at index #0).
454 /// var action1Controls = action1.controls;
455 ///
456 /// var action2 = new InputAction();
457 /// action2.AddBinding("<Gamepad>/buttonSouth");
458 /// // Add a binding that implicitly matches the first binding, too. When binding resolution
459 /// // happens, this binding will only receive buttonNorth, buttonWest, and buttonEast, but not
460 /// // buttonSouth as the first binding already received that control.
461 /// action2.AddBinding("<Gamepad>/button*");
462 ///
463 /// // Contains only all four face buttons (buttonSouth, buttonNorth, buttonEast, buttonWest)
464 /// // but buttonSouth is associated with the first button and only buttonNorth, buttonEast,
465 /// // and buttonWest are associated with the second binding.
466 /// var action2Controls = action2.controls;
467 /// </code>
468 /// </example>
469 /// </remarks>
470 /// <seealso cref="InputActionRebindingExtensions.GetBindingIndexForControl"/>
471 /// <seealso cref="bindings"/>
472 public ReadOnlyArray<InputControl> controls
473 {
474 get
475 {
476 var map = GetOrCreateActionMap();
477 map.ResolveBindingsIfNecessary();
478 return map.GetControlsForSingleAction(this);
479 }
480 }
481
482 /// <summary>
483 /// The current phase of the action.
484 /// </summary>
485 /// <remarks>
486 /// When listening for control input and when responding to control value changes,
487 /// actions will go through several possible phases.
488 ///
489 /// In general, when an action starts receiving input, it will go to <see cref="InputActionPhase.Started"/>
490 /// and when it stops receiving input, it will go to <see cref="InputActionPhase.Canceled"/>.
491 /// When <see cref="InputActionPhase.Performed"/> is used depends primarily on the type
492 /// of action. <see cref="InputActionType.Value"/> will trigger <see cref="InputActionPhase.Performed"/>
493 /// whenever the value of the control changes (including the first time; i.e. it will first
494 /// trigger <see cref="InputActionPhase.Started"/> and then <see cref="InputActionPhase.Performed"/>
495 /// right after) whereas <see cref="InputActionType.Button"/> will trigger <see cref="InputActionPhase.Performed"/>
496 /// as soon as the button press threshold (<see cref="InputSettings.defaultButtonPressPoint"/>)
497 /// has been crossed.
498 ///
499 /// Note that both interactions and the action <see cref="type"/> can affect the phases
500 /// that an action goes through. <see cref="InputActionType.PassThrough"/> actions will
501 /// only ever use <see cref="InputActionPhase.Performed"/> and not go to <see
502 /// cref="InputActionPhase.Started"/> or <see cref="InputActionPhase.Canceled"/> (as
503 /// pass-through actions do not follow the start-performed-canceled model in general).
504 ///
505 /// While an action is disabled, its phase is <see cref="InputActionPhase.Disabled"/>.
506 /// </remarks>
507 public InputActionPhase phase => currentState.phase;
508
509 /// <summary>
510 /// True if the action is currently in <see cref="InputActionPhase.Started"/> or <see cref="InputActionPhase.Performed"/>
511 /// phase. False in all other cases.
512 /// </summary>
513 /// <see cref="phase"/>
514 public bool inProgress => phase.IsInProgress();
515
516 /// <summary>
517 /// Whether the action is currently enabled, i.e. responds to input, or not.
518 /// </summary>
519 /// <value>True if the action is currently enabled.</value>
520 /// <remarks>
521 /// An action is enabled by either calling <see cref="Enable"/> on it directly or by calling
522 /// <see cref="InputActionMap.Enable"/> on the <see cref="InputActionMap"/> containing the action.
523 /// When enabled, an action will listen for changes on the controls it is bound to and trigger
524 /// callbacks such as <see cref="started"/>, <see cref="performed"/>, and <see cref="canceled"/>
525 /// in response.
526 /// </remarks>
527 /// <seealso cref="Enable"/>
528 /// <seealso cref="Disable"/>
529 /// <seealso cref="InputActionMap.Enable"/>
530 /// <seealso cref="InputActionMap.Disable"/>
531 /// <seealso cref="InputSystem.ListEnabledActions()"/>
532 public bool enabled => phase != InputActionPhase.Disabled;
533
534 /// <summary>
535 /// Event that is triggered when the action has been started.
536 /// </summary>
537 /// <remarks>
538 /// See <see cref="phase"/> for details of how an action progresses through phases
539 /// and triggers this callback.
540 /// </remarks>
541 /// <see cref="InputActionPhase.Started"/>
542 public event Action<CallbackContext> started
543 {
544 add => m_OnStarted.AddCallback(value);
545 remove => m_OnStarted.RemoveCallback(value);
546 }
547
548 /// <summary>
549 /// Event that is triggered when the action has been <see cref="started"/>
550 /// but then canceled before being fully <see cref="performed"/>.
551 /// </summary>
552 /// <remarks>
553 /// See <see cref="phase"/> for details of how an action progresses through phases
554 /// and triggers this callback.
555 /// </remarks>
556 /// <see cref="InputActionPhase.Canceled"/>
557 public event Action<CallbackContext> canceled
558 {
559 add => m_OnCanceled.AddCallback(value);
560 remove => m_OnCanceled.RemoveCallback(value);
561 }
562
563 /// <summary>
564 /// Event that is triggered when the action has been fully performed.
565 /// </summary>
566 /// <remarks>
567 /// See <see cref="phase"/> for details of how an action progresses through phases
568 /// and triggers this callback.
569 /// </remarks>
570 /// <see cref="InputActionPhase.Performed"/>
571 public event Action<CallbackContext> performed
572 {
573 add => m_OnPerformed.AddCallback(value);
574 remove => m_OnPerformed.RemoveCallback(value);
575 }
576
577 ////TODO: Obsolete and drop this when we can break API
578 /// <summary>
579 /// Equivalent to <see cref="WasPerformedThisFrame"/>.
580 /// </summary>
581 /// <seealso cref="WasPerformedThisFrame"/>
582 public bool triggered => WasPerformedThisFrame();
583
584 /// <summary>
585 /// The currently active control that is driving the action. <see langword="null"/> while the action
586 /// is in waiting (<see cref="InputActionPhase.Waiting"/>) or canceled (<see cref="InputActionPhase.Canceled"/>)
587 /// state. Otherwise the control that last had activity on it which wasn't ignored.
588 /// </summary>
589 /// <remarks>
590 /// Note that the control's value does not necessarily correspond to the value of the
591 /// action (<see cref="ReadValue{TValue}"/>) as the control may be part of a composite.
592 /// </remarks>
593 /// <seealso cref="CallbackContext.control"/>
594 public unsafe InputControl activeControl
595 {
596 get
597 {
598 var state = GetOrCreateActionMap().m_State;
599 if (state != null)
600 {
601 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
602 var controlIndex = actionStatePtr->controlIndex;
603 if (controlIndex != InputActionState.kInvalidIndex)
604 return state.controls[controlIndex];
605 }
606 return null;
607 }
608 }
609
610 /// <summary>
611 /// Type of value returned by <see cref="ReadValueAsObject"/> and currently expected
612 /// by <see cref="ReadValue{TValue}"/>. <see langword="null"/> while the action
613 /// is in waiting (<see cref="InputActionPhase.Waiting"/>) or canceled (<see cref="InputActionPhase.Canceled"/>)
614 /// state as this is based on the currently active control that is driving the action.
615 /// </summary>
616 /// <value>Type of object returned when reading a value.</value>
617 /// <remarks>
618 /// The type of value returned by an action is usually determined by the
619 /// <see cref="InputControl"/> that triggered the action, i.e. by the
620 /// control referenced from <see cref="activeControl"/>.
621 ///
622 /// However, if the binding that triggered is a composite, then the composite
623 /// will determine values and not the individual control that triggered (that
624 /// one just feeds values into the composite).
625 ///
626 /// The active value type may change depending on which controls are actuated if there are multiple
627 /// bindings with different control types. This property can be used to ensure you are calling the
628 /// <see cref="ReadValue{TValue}"/> method with the expected type parameter if your action is
629 /// configured to allow multiple control types as otherwise that method will throw an <see cref="InvalidOperationException"/>
630 /// if the type of the control that triggered the action does not match the type parameter.
631 /// </remarks>
632 /// <seealso cref="InputControl.valueType"/>
633 /// <seealso cref="InputBindingComposite.valueType"/>
634 /// <seealso cref="activeControl"/>
635 public unsafe Type activeValueType
636 {
637 get
638 {
639 var state = GetOrCreateActionMap().m_State;
640 if (state != null)
641 {
642 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
643 var controlIndex = actionStatePtr->controlIndex;
644 if (controlIndex != InputActionState.kInvalidIndex)
645 return state.GetValueType(actionStatePtr->bindingIndex, controlIndex);
646 }
647
648 return null;
649 }
650 }
651
652 /// <summary>
653 /// Whether the action wants a state check on its bound controls as soon as it is enabled. This is always
654 /// true for <see cref="InputActionType.Value"/> actions but can optionally be enabled for <see cref="InputActionType.Button"/>
655 /// or <see cref="InputActionType.PassThrough"/> actions.
656 /// </summary>
657 /// <remarks>
658 /// Usually, when an action is <see cref="enabled"/> (e.g. via <see cref="Enable"/>), it will start listening for input
659 /// and then trigger once the first input arrives. However, <see cref="controls"/> bound to an action may already be
660 /// actuated when an action is enabled. For example, if a "jump" action is bound to <see cref="Keyboard.spaceKey"/>,
661 /// the space bar may already be pressed when the jump action is enabled.
662 ///
663 /// <see cref="InputActionType.Value"/> actions handle this differently by immediately performing an "initial state check"
664 /// in the next input update (see <see cref="InputSystem.Update"/>) after being enabled. If any of the bound controls
665 /// is already actuated, the action will trigger right away -- even with no change in state on the controls.
666 ///
667 /// This same behavior can be enabled explicitly for <see cref="InputActionType.Button"/> and <see cref="InputActionType.PassThrough"/>
668 /// actions using this property.
669 /// </remarks>
670 /// <seealso cref="Enable"/>
671 /// <seealso cref="InputActionType.Value"/>
672 public bool wantsInitialStateCheck
673 {
674 get => type == InputActionType.Value || (m_Flags & ActionFlags.WantsInitialStateCheck) != 0;
675 set
676 {
677 if (value)
678 m_Flags |= ActionFlags.WantsInitialStateCheck;
679 else
680 m_Flags &= ~ActionFlags.WantsInitialStateCheck;
681 }
682 }
683
684 /// <summary>
685 /// Construct an unnamed, free-standing action that is not part of any map or asset
686 /// and has no bindings. Bindings can be added with <see
687 /// cref="InputActionSetupExtensions.AddBinding(InputAction,string,string,string,string)"/>.
688 /// The action type defaults to <see cref="InputActionType.Value"/>.
689 /// </summary>
690 /// <remarks>
691 /// The action will not have an associated <see cref="InputActionMap"/> and <see cref="actionMap"/>
692 /// will thus be <c>null</c>. Use <see cref="InputActionSetupExtensions.AddAction"/> instead if
693 /// you want to add a new action to an action map.
694 ///
695 /// The action will remain disabled after construction and thus not listen/react to input yet.
696 /// Use <see cref="Enable"/> to enable the action.
697 ///
698 /// <example>
699 /// <code>
700 /// // Create an action with two bindings.
701 /// var action = new InputAction();
702 /// action.AddBinding("<Gamepad>/leftStick");
703 /// action.AddBinding("<Mouse>/delta");
704 ///
705 /// action.performed += ctx => Debug.Log("Value: " + ctx.ReadValue<Vector2>());
706 ///
707 /// action.Enable();
708 /// </code>
709 /// </example>
710 /// </remarks>
711 public InputAction()
712 {
713 m_Id = Guid.NewGuid().ToString();
714 }
715
716 /// <summary>
717 /// Construct a free-standing action that is not part of an <see cref="InputActionMap"/>.
718 /// </summary>
719 /// <param name="name">Name of the action. If null or empty, the action will be unnamed.</param>
720 /// <param name="type">Type of action to create. Defaults to <see cref="InputActionType.Value"/>, i.e.
721 /// an action that provides continuous values.</param>
722 /// <param name="binding">If not null or empty, a binding with the given path will be added to the action
723 /// right away. The format of the string is the as for <see cref="InputBinding.path"/>.</param>
724 /// <param name="interactions">If <paramref name="binding"/> is not null or empty, this parameter represents
725 /// the interaction to apply to the newly created binding (i.e. <see cref="InputBinding.interactions"/>). If
726 /// <paramref name="binding"/> is not supplied, this parameter represents the interactions to apply to the action
727 /// (i.e. the value of <see cref="interactions"/>).</param>
728 /// <param name="processors">If <paramref name="binding"/> is not null or empty, this parameter represents
729 /// the processors to apply to the newly created binding (i.e. <see cref="InputBinding.processors"/>). If
730 /// <paramref name="binding"/> is not supplied, this parameter represents the processors to apply to the
731 /// action (i.e. the value of <see cref="processors"/>).</param>
732 /// <param name="expectedControlType">The optional expected control type for the action (i.e. <see
733 /// cref="expectedControlType"/>).</param>
734 /// <remarks>
735 /// The action will not have an associated <see cref="InputActionMap"/> and <see cref="actionMap"/>
736 /// will thus be <c>null</c>. Use <see cref="InputActionSetupExtensions.AddAction"/> instead if
737 /// you want to add a new action to an action map.
738 ///
739 /// The action will remain disabled after construction and thus not listen/react to input yet.
740 /// Use <see cref="Enable"/> to enable the action.
741 ///
742 /// Additional bindings can be added with <see
743 /// cref="InputActionSetupExtensions.AddBinding(InputAction,string,string,string,string)"/>.
744 ///
745 /// <example>
746 /// <code>
747 /// // Create a button action responding to the gamepad A button.
748 /// var action = new InputAction(type: InputActionType.Button, binding: "<Gamepad>/buttonSouth");
749 /// action.performed += ctx => Debug.Log("Pressed");
750 /// action.Enable();
751 /// </code>
752 /// </example>
753 /// </remarks>
754 public InputAction(string name = null, InputActionType type = default, string binding = null,
755 string interactions = null, string processors = null, string expectedControlType = null)
756 {
757 m_Name = name;
758 m_Type = type;
759
760 if (!string.IsNullOrEmpty(binding))
761 {
762 m_SingletonActionBindings = new[]
763 {
764 new InputBinding
765 {
766 path = binding,
767 interactions = interactions,
768 processors = processors,
769 action = m_Name,
770 id = Guid.NewGuid(),
771 },
772 };
773 m_BindingsStartIndex = 0;
774 m_BindingsCount = 1;
775 }
776 else
777 {
778 m_Interactions = interactions;
779 m_Processors = processors;
780 }
781
782 m_ExpectedControlType = expectedControlType;
783 m_Id = Guid.NewGuid().ToString();
784 }
785
786 /// <summary>
787 /// Release internal state held on to by the action.
788 /// </summary>
789 /// <remarks>
790 /// Once enabled, actions will allocate a block of state internally that they will hold on to
791 /// until disposed of. For free-standing actions, that state is private to just the action.
792 /// For actions that are part of <see cref="InputActionMap"/>s, the state is shared by all
793 /// actions in the map and, if the map itself is part of an <see cref="InputActionAsset"/>,
794 /// also by all the maps that are part of the asset.
795 ///
796 /// Note that the internal state holds on to GC heap memory as well as memory from the
797 /// unmanaged, C++ heap.
798 /// </remarks>
799 public void Dispose()
800 {
801 m_ActionMap?.m_State?.Dispose();
802 }
803
804 /// <summary>
805 /// Return a string version of the action. Mainly useful for debugging.
806 /// </summary>
807 /// <returns>A string version of the action.</returns>
808 public override string ToString()
809 {
810 string str;
811 if (m_Name == null)
812 str = "<Unnamed>";
813 else if (m_ActionMap != null && !isSingletonAction && !string.IsNullOrEmpty(m_ActionMap.name))
814 str = $"{m_ActionMap.name}/{m_Name}";
815 else
816 str = m_Name;
817
818 var controls = this.controls;
819 if (controls.Count > 0)
820 {
821 str += "[";
822 var isFirst = true;
823 foreach (var control in controls)
824 {
825 if (!isFirst)
826 str += ",";
827 str += control.path;
828 isFirst = false;
829 }
830 str += "]";
831 }
832
833 return str;
834 }
835
836 /// <summary>
837 /// Enable the action such that it actively listens for input and runs callbacks
838 /// in response.
839 /// </summary>
840 /// <remarks>
841 /// If the action is already enabled, this method does nothing.
842 ///
843 /// By default, actions start out disabled, i.e. with <see cref="enabled"/> being false.
844 /// When enabled, two things happen.
845 ///
846 /// First, if it hasn't already happened, an action will resolve all of its bindings
847 /// to <see cref="InputControl"/>s. This also happens if, since the action was last enabled,
848 /// the setup of devices in the system has changed such that it may impact the action.
849 ///
850 /// Second, for all the <see cref="controls"/> bound to an action, change monitors (see
851 /// <see cref="IInputStateChangeMonitor"/>) will be added to the system. If any of the
852 /// controls changes state in the future, the action will get notified and respond.
853 ///
854 /// <see cref="InputActionType.Value"/> type actions will also perform an initial state
855 /// check in the input system update following the call to Enable. This means that if
856 /// any of the bound controls are already actuated and produce a non-<c>default</c> value,
857 /// the action will immediately trigger in response.
858 ///
859 /// Note that this method only enables a single action. This is also allowed for action
860 /// that are part of an <see cref="InputActionMap"/>. To enable all actions in a map,
861 /// call <see cref="InputActionMap.Enable"/>.
862 ///
863 /// The <see cref="InputActionMap"/> associated with an action (if any), will immediately
864 /// toggle to being enabled (see <see cref="InputActionMap.enabled"/>) as soon as the first
865 /// action in the map is enabled and for as long as any action in the map is still enabled.
866 ///
867 /// The first time an action is enabled, it will allocate a block of state internally that it
868 /// will hold on to until disposed of. For free-standing actions, that state is private to
869 /// just the action. For actions that are part of <see cref="InputActionMap"/>s, the state
870 /// is shared by all actions in the map and, if the map itself is part of an <see
871 /// cref="InputActionAsset"/>, also by all the maps that are part of the asset.
872 ///
873 /// To dispose of the state, call <see cref="Dispose"/>.
874 ///
875 /// <example>
876 /// <code>
877 /// var gamepad = InputSystem.AddDevice<Gamepad>();
878 ///
879 /// var action = new InputAction(type: InputActionType.Value, binding: "<Gamepad>/leftTrigger");
880 /// action.performed = ctx => Debug.Log("Action triggered!");
881 ///
882 /// // Perform some fake input on the gamepad. Note that the action
883 /// // will *NOT* get triggered as it is not enabled.
884 /// // NOTE: We use Update() here only for demonstration purposes. In most cases,
885 /// // it's not a good method to call directly as it basically injects artificial
886 /// // input frames into the player loop. Usually a recipe for breakage.
887 /// InputSystem.QueueStateEvent(gamepad, new GamepadState { leftTrigger = 0.5f });
888 /// InputSystem.Update();
889 ///
890 /// action.Enable();
891 ///
892 /// // Now, with the left trigger already being down and the action enabled, it will
893 /// // trigger in the next frame.
894 /// InputSystem.Update();
895 /// </code>
896 /// </example>
897 /// </remarks>
898 /// <seealso cref="Disable"/>
899 /// <seealso cref="enabled"/>
900 public void Enable()
901 {
902 if (enabled)
903 return;
904
905 // For singleton actions, we create an internal-only InputActionMap
906 // private to the action.
907 var map = GetOrCreateActionMap();
908
909 // First time we're enabled, find all controls.
910 map.ResolveBindingsIfNecessary();
911
912 // Go live.
913 map.m_State.EnableSingleAction(this);
914 }
915
916 /// <summary>
917 /// Disable the action such that is stop listening/responding to input.
918 /// </summary>
919 /// <remarks>
920 /// If the action is already disabled, this method does nothing.
921 ///
922 /// If the action is currently in progress, i.e. if <see cref="phase"/> is
923 /// <see cref="InputActionPhase.Started"/>, the action will be canceled as
924 /// part of being disabled. This means that you will see a call on <see cref="canceled"/>
925 /// from within the call to <c>Disable()</c>.
926 /// </remarks>
927 /// <seealso cref="enabled"/>
928 /// <seealso cref="Enable"/>
929 public void Disable()
930 {
931 if (!enabled)
932 return;
933
934 m_ActionMap.m_State.DisableSingleAction(this);
935 }
936
937 ////REVIEW: is *not* cloning IDs here really the right thing to do?
938 /// <summary>
939 /// Return an identical instance of the action.
940 /// </summary>
941 /// <returns>An identical clone of the action</returns>
942 /// <remarks>
943 /// Note that if you clone an action that is part of an <see cref="InputActionMap"/>,
944 /// you will not get a new action that is part of the same map. Instead, you will
945 /// get a free-standing action not associated with any action map.
946 ///
947 /// Also, note that the <see cref="id"/> of the action is not cloned. Instead, the
948 /// clone will receive a new unique ID. Also, callbacks install on events such
949 /// as <see cref="started"/> will not be copied over to the clone.
950 /// </remarks>
951 public InputAction Clone()
952 {
953 var clone = new InputAction(name: m_Name, type: m_Type)
954 {
955 m_SingletonActionBindings = bindings.ToArray(),
956 m_BindingsCount = m_BindingsCount,
957 m_ExpectedControlType = m_ExpectedControlType,
958 m_Interactions = m_Interactions,
959 m_Processors = m_Processors,
960 m_Flags = m_Flags,
961 };
962 return clone;
963 }
964
965 /// <summary>
966 /// Return an boxed instance of the action.
967 /// </summary>
968 /// <returns>An boxed clone of the action</returns>
969 /// <seealso cref="Clone"/>
970 object ICloneable.Clone()
971 {
972 return Clone();
973 }
974
975 ////TODO: ReadValue(void*, int)
976
977 /// <summary>
978 /// Read the current value of the control that is driving this action. If no bound control is actuated, returns
979 /// default(TValue), but note that binding processors are always applied.
980 /// </summary>
981 /// <typeparam name="TValue">Value type to read. Must match the value type of the binding/control that triggered.</typeparam>
982 /// <returns>The current value of the control/binding that is driving this action with all binding processors applied.</returns>
983 /// <remarks>
984 /// This method can be used as an alternative to hooking into <see cref="started"/>, <see cref="performed"/>,
985 /// and/or <see cref="canceled"/> and reading out the value using <see cref="CallbackContext.ReadValue{TValue}"/>
986 /// there. Instead, this API acts more like a polling API that can be called, for example, as part of
987 /// <c>MonoBehaviour.Update</c>.
988 ///
989 /// <example>
990 /// <code>
991 /// // Let's say you have a MyControls.inputactions file with "Generate C# Class" enabled
992 /// // and it has an action map called "gameplay" with a "move" action of type Vector2.
993 /// public class MyBehavior : MonoBehaviour
994 /// {
995 /// public MyControls controls;
996 /// public float moveSpeed = 4;
997 ///
998 /// protected void Awake()
999 /// {
1000 /// controls = new MyControls();
1001 /// }
1002 ///
1003 /// protected void OnEnable()
1004 /// {
1005 /// controls.gameplay.Enable();
1006 /// }
1007 ///
1008 /// protected void OnDisable()
1009 /// {
1010 /// controls.gameplay.Disable();
1011 /// }
1012 ///
1013 /// protected void Update()
1014 /// {
1015 /// var moveVector = controls.gameplay.move.ReadValue<Vector2>() * (moveSpeed * Time.deltaTime);
1016 /// //...
1017 /// }
1018 /// }
1019 /// </code>
1020 /// </example>
1021 ///
1022 /// If the action has button-like behavior, then <see cref="triggered"/> is usually a better alternative to
1023 /// reading out a float and checking if it is above the button press point.
1024 /// </remarks>
1025 /// <exception cref="InvalidOperationException">The given <typeparamref name="TValue"/> type does not match
1026 /// the value type of the control or composite currently driving the action.</exception>
1027 /// <seealso cref="triggered"/>
1028 /// <seealso cref="ReadValueAsObject"/>
1029 /// <seealso cref="CallbackContext.ReadValue{TValue}"/>
1030 public unsafe TValue ReadValue<TValue>()
1031 where TValue : struct
1032 {
1033 var state = GetOrCreateActionMap().m_State;
1034 if (state == null)
1035 return default(TValue);
1036
1037 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
1038 return actionStatePtr->phase.IsInProgress()
1039 ? state.ReadValue<TValue>(actionStatePtr->bindingIndex, actionStatePtr->controlIndex)
1040 : state.ApplyProcessors(actionStatePtr->bindingIndex, default(TValue));
1041 }
1042
1043 /// <summary>
1044 /// Same as <see cref="ReadValue{TValue}"/> but read the value without having to know the value type
1045 /// of the action.
1046 /// </summary>
1047 /// <returns>The current value of the action or <c>null</c> if the action is not currently in <see cref="InputActionPhase.Started"/>
1048 /// or <see cref="InputActionPhase.Performed"/> phase.</returns>
1049 /// <remarks>
1050 /// This method allocates GC memory and is thus not a good choice for getting called as part of gameplay
1051 /// logic.
1052 /// </remarks>
1053 /// <seealso cref="ReadValue{TValue}"/>
1054 /// <seealso cref="InputAction.CallbackContext.ReadValueAsObject"/>
1055 public unsafe object ReadValueAsObject()
1056 {
1057 var state = GetOrCreateActionMap().m_State;
1058 if (state == null)
1059 return null;
1060
1061 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
1062 if (actionStatePtr->phase.IsInProgress())
1063 {
1064 var controlIndex = actionStatePtr->controlIndex;
1065 if (controlIndex != InputActionState.kInvalidIndex)
1066 return state.ReadValueAsObject(actionStatePtr->bindingIndex, controlIndex);
1067 }
1068
1069 return null;
1070 }
1071
1072 /// <summary>
1073 /// Read the current amount of actuation of the control that is driving this action.
1074 /// </summary>
1075 /// <returns>Returns the current level of control actuation (usually [0..1]) or -1 if
1076 /// the control is actuated but does not support computing magnitudes.</returns>
1077 /// <remarks>
1078 /// Magnitudes do not make sense for all types of controls. Controls that have no meaningful magnitude
1079 /// will return -1 when calling this method. Any negative magnitude value should be considered an invalid value.
1080 /// <br />
1081 /// The magnitude returned by an action is usually determined by the
1082 /// <see cref="InputControl"/> that triggered the action, i.e. by the
1083 /// control referenced from <see cref="activeControl"/>.
1084 /// <br />
1085 /// However, if the binding that triggered is a composite, then the composite
1086 /// will determine the magnitude and not the individual control that triggered.
1087 /// Instead, the value of the control that triggered the action will be fed into the composite magnitude calculation.
1088 /// </remarks>
1089 /// <seealso cref="InputControl.EvaluateMagnitude()"/>
1090 /// <seealso cref="InputBindingComposite.EvaluateMagnitude"/>
1091 public unsafe float GetControlMagnitude()
1092 {
1093 var state = GetOrCreateActionMap().m_State;
1094 if (state != null)
1095 {
1096 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
1097 if (actionStatePtr->haveMagnitude)
1098 return actionStatePtr->magnitude;
1099 }
1100
1101 return 0f;
1102 }
1103
1104 /// <summary>
1105 /// Reset the action state to default.
1106 /// </summary>
1107 /// <remarks>
1108 /// This method can be used to forcibly cancel an action even while it is in progress. Note that unlike
1109 /// disabling an action, for example, this also effects APIs such as <see cref="WasPressedThisFrame"/>.
1110 ///
1111 /// Note that invoking this method will not modify enabled state.
1112 /// </remarks>
1113 /// <seealso cref="inProgress"/>
1114 /// <seealso cref="phase"/>
1115 /// <seealso cref="Enable"/>
1116 /// <seealso cref="Disable"/>
1117 public void Reset()
1118 {
1119 var state = GetOrCreateActionMap().m_State;
1120 state?.ResetActionState(m_ActionIndexInState, toPhase: enabled ? InputActionPhase.Waiting : InputActionPhase.Disabled, hardReset: true);
1121 }
1122
1123 /// <summary>
1124 /// Check whether the current actuation of the action has crossed the button press threshold (see
1125 /// <see cref="InputSettings.defaultButtonPressPoint"/>) and has not yet fallen back below the
1126 /// release threshold (see <see cref="InputSettings.buttonReleaseThreshold"/>).
1127 /// </summary>
1128 /// <returns>True if the action is considered to be in "pressed" state, false otherwise.</returns>
1129 /// <remarks>
1130 /// This method is different from simply reading the action's current <c>float</c> value and comparing
1131 /// it to the press threshold and is also different from comparing the current actuation of
1132 /// <see cref="activeControl"/> to it. This is because the current level of actuation might have already
1133 /// fallen below the press threshold but might not yet have reached the release threshold.
1134 ///
1135 /// This method works with any <see cref="type"/> of action, not just buttons.
1136 ///
1137 /// Also note that because this operates on the results of <see cref="InputControl.EvaluateMagnitude()"/>,
1138 /// it works with many kind of controls, not just buttons. For example, if an action is bound
1139 /// to a <see cref="StickControl"/>, the control will be considered "pressed" once the magnitude
1140 /// of the Vector2 of the control has crossed the press threshold.
1141 ///
1142 /// Finally, note that custom button press points of controls (see <see cref="ButtonControl.pressPoint"/>)
1143 /// are respected and will take precedence over <see cref="InputSettings.defaultButtonPressPoint"/>.
1144 ///
1145 /// <example>
1146 /// <code>
1147 /// var up = playerInput.actions["up"];
1148 /// if (up.IsPressed())
1149 /// transform.Translate(0, 10 * Time.deltaTime, 0);
1150 /// </code>
1151 /// </example>
1152 ///
1153 /// Disabled actions will always return false from this method, even if a control bound to the action
1154 /// is currently pressed. Also, re-enabling an action will not restore the state to when the action
1155 /// was disabled even if the control is still actuated.
1156 /// </remarks>
1157 /// <seealso cref="InputSettings.defaultButtonPressPoint"/>
1158 /// <seealso cref="ButtonControl.pressPoint"/>
1159 /// <seealso cref="CallbackContext.ReadValueAsButton"/>
1160 /// <seealso cref="WasPressedThisFrame"/>
1161 /// <seealso cref="WasReleasedThisFrame"/>
1162 public unsafe bool IsPressed()
1163 {
1164 var state = GetOrCreateActionMap().m_State;
1165 if (state != null)
1166 {
1167 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
1168 return actionStatePtr->isPressed;
1169 }
1170 return false;
1171 }
1172
1173 /// <summary>
1174 /// Whether the action has been <see cref="InputActionPhase.Started"/> or <see cref="InputActionPhase.Performed"/>.
1175 /// </summary>
1176 /// <returns>True if the action is currently triggering.</returns>
1177 /// <seealso cref="phase"/>
1178 public unsafe bool IsInProgress()
1179 {
1180 var state = GetOrCreateActionMap().m_State;
1181 if (state != null)
1182 {
1183 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
1184 return actionStatePtr->phase.IsInProgress();
1185 }
1186 return false;
1187 }
1188
1189 /// <summary>
1190 /// Returns true if the action's value crossed the press threshold (see <see cref="InputSettings.defaultButtonPressPoint"/>)
1191 /// at any point in the frame.
1192 /// </summary>
1193 /// <returns>True if the action was pressed this frame.</returns>
1194 /// <remarks>
1195 /// This method is different from <see cref="WasPerformedThisFrame"/> in that it is not bound
1196 /// to <see cref="phase"/>. Instead, if the action's level of actuation (that is, the level of
1197 /// magnitude -- see <see cref="InputControl.EvaluateMagnitude()"/> -- of the control(s) bound
1198 /// to the action) crossed the press threshold (see <see cref="InputSettings.defaultButtonPressPoint"/>)
1199 /// at any point in the frame, this method will return true. It will do so even if there is an
1200 /// interaction on the action that has not yet performed the action in response to the press.
1201 ///
1202 /// This method works with any <see cref="type"/> of action, not just buttons.
1203 ///
1204 /// Also note that because this operates on the results of <see cref="InputControl.EvaluateMagnitude()"/>,
1205 /// it works with many kind of controls, not just buttons. For example, if an action is bound
1206 /// to a <see cref="StickControl"/>, the control will be considered "pressed" once the magnitude
1207 /// of the Vector2 of the control has crossed the press threshold.
1208 ///
1209 /// Finally, note that custom button press points of controls (see <see cref="ButtonControl.pressPoint"/>)
1210 /// are respected and will take precedence over <see cref="InputSettings.defaultButtonPressPoint"/>.
1211 ///
1212 /// <example>
1213 /// <code>
1214 /// var fire = playerInput.actions["fire"];
1215 /// if (fire.WasPressedThisFrame() && fire.IsPressed())
1216 /// StartFiring();
1217 /// else if (fire.WasReleasedThisFrame())
1218 /// StopFiring();
1219 /// </code>
1220 /// </example>
1221 ///
1222 /// This method will disregard whether the action is currently enabled or disabled. It will keep returning
1223 /// true for the duration of the frame even if the action was subsequently disabled in the frame.
1224 ///
1225 /// The meaning of "frame" is either the current "dynamic" update (<c>MonoBehaviour.Update</c>) or the current
1226 /// fixed update (<c>MonoBehaviour.FixedUpdate</c>) depending on the value of the <see cref="InputSettings.updateMode"/> setting.
1227 /// </remarks>
1228 /// <seealso cref="IsPressed"/>
1229 /// <seealso cref="WasReleasedThisFrame"/>
1230 /// <seealso cref="CallbackContext.ReadValueAsButton"/>
1231 /// <seealso cref="WasPerformedThisFrame"/>
1232 public unsafe bool WasPressedThisFrame()
1233 {
1234 var state = GetOrCreateActionMap().m_State;
1235 if (state != null)
1236 {
1237 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
1238 var currentUpdateStep = InputUpdate.s_UpdateStepCount;
1239 return actionStatePtr->pressedInUpdate == currentUpdateStep && currentUpdateStep != default && actionStatePtr->frame == Time.frameCount;
1240 }
1241
1242 return false;
1243 }
1244
1245 /// <summary>
1246 /// Returns true if the action's value crossed the release threshold (see <see cref="InputSettings.buttonReleaseThreshold"/>)
1247 /// at any point in the frame after being in pressed state.
1248 /// </summary>
1249 /// <returns>True if the action was released this frame.</returns>
1250 /// <remarks>
1251 /// This method works with any <see cref="type"/> of action, not just buttons.
1252 ///
1253 /// Also note that because this operates on the results of <see cref="InputControl.EvaluateMagnitude()"/>,
1254 /// it works with many kind of controls, not just buttons. For example, if an action is bound
1255 /// to a <see cref="StickControl"/>, the control will be considered "pressed" once the magnitude
1256 /// of the Vector2 of the control has crossed the press threshold.
1257 ///
1258 /// Finally, note that custom button press points of controls (see <see cref="ButtonControl.pressPoint"/>)
1259 /// are respected and will take precedence over <see cref="InputSettings.defaultButtonPressPoint"/>.
1260 ///
1261 /// <example>
1262 /// <code>
1263 /// var fire = playerInput.actions["fire"];
1264 /// if (fire.WasPressedThisFrame() && fire.IsPressed())
1265 /// StartFiring();
1266 /// else if (fire.WasReleasedThisFrame())
1267 /// StopFiring();
1268 /// </code>
1269 /// </example>
1270 ///
1271 /// This method will disregard whether the action is currently enabled or disabled. It will keep returning
1272 /// true for the duration of the frame even if the action was subsequently disabled in the frame.
1273 ///
1274 /// The meaning of "frame" is either the current "dynamic" update (<c>MonoBehaviour.Update</c>) or the current
1275 /// fixed update (<c>MonoBehaviour.FixedUpdate</c>) depending on the value of the <see cref="InputSettings.updateMode"/> setting.
1276 /// </remarks>
1277 /// <seealso cref="IsPressed"/>
1278 /// <seealso cref="WasPressedThisFrame"/>
1279 /// <seealso cref="CallbackContext.ReadValueAsButton"/>
1280 /// <seealso cref="WasCompletedThisFrame"/>
1281 public unsafe bool WasReleasedThisFrame()
1282 {
1283 var state = GetOrCreateActionMap().m_State;
1284 if (state != null)
1285 {
1286 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
1287 var currentUpdateStep = InputUpdate.s_UpdateStepCount;
1288 return actionStatePtr->releasedInUpdate == currentUpdateStep && currentUpdateStep != default && actionStatePtr->frame == Time.frameCount;
1289 }
1290
1291 return false;
1292 }
1293
1294 ////REVIEW: Should we also have WasStartedThisFrame()? (and WasCanceledThisFrame()?)
1295
1296 /// <summary>
1297 /// Check whether <see cref="phase"/> was <see cref="InputActionPhase.Performed"/> at any point
1298 /// in the current frame.
1299 /// </summary>
1300 /// <returns>True if the action performed this frame.</returns>
1301 /// <remarks>
1302 /// This method is different from <see cref="WasPressedThisFrame"/> in that it depends directly on the
1303 /// interaction(s) driving the action (including the default interaction if no specific interaction
1304 /// has been added to the action or binding).
1305 ///
1306 /// For example, let's say the action is bound to the space bar and that the binding has a
1307 /// <see cref="Interactions.HoldInteraction"/> assigned to it. In the frame where the space bar
1308 /// is pressed, <see cref="WasPressedThisFrame"/> will be true (because the button/key is now pressed)
1309 /// but <c>WasPerformedThisFrame</c> will still be false (because the hold has not been performed yet).
1310 /// Only after the hold time has expired will <c>WasPerformedThisFrame</c> be true and only in the frame
1311 /// where the hold performed.
1312 ///
1313 /// This is different from checking <see cref="phase"/> directly as the action might have already progressed
1314 /// to a different phase after performing. In other words, even if an action performed in a frame, <see cref="phase"/>
1315 /// might no longer be <see cref="InputActionPhase.Performed"/>, whereas <c>WasPerformedThisFrame</c> will remain
1316 /// true for the entirety of the frame regardless of what else the action does.
1317 ///
1318 /// Unlike <see cref="ReadValue{TValue}"/>, which will reset when the action goes back to waiting
1319 /// state, this property will stay true for the duration of the current frame (that is, until the next
1320 /// <see cref="InputSystem.Update"/> runs) as long as the action was triggered at least once.
1321 ///
1322 /// <example>
1323 /// <code>
1324 /// var warp = playerInput.actions["Warp"];
1325 /// if (warp.WasPerformedThisFrame())
1326 /// InitiateWarp();
1327 /// </code>
1328 /// </example>
1329 ///
1330 /// This method will disregard whether the action is currently enabled or disabled. It will keep returning
1331 /// true for the duration of the frame even if the action was subsequently disabled in the frame.
1332 ///
1333 /// The meaning of "frame" is either the current "dynamic" update (<c>MonoBehaviour.Update</c>) or the current
1334 /// fixed update (<c>MonoBehaviour.FixedUpdate</c>) depending on the value of the <see cref="InputSettings.updateMode"/> setting.
1335 /// </remarks>
1336 /// <seealso cref="WasCompletedThisFrame"/>
1337 /// <seealso cref="WasPressedThisFrame"/>
1338 /// <seealso cref="phase"/>
1339 public unsafe bool WasPerformedThisFrame()
1340 {
1341 var state = GetOrCreateActionMap().m_State;
1342
1343 if (state != null)
1344 {
1345 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
1346 var currentUpdateStep = InputUpdate.s_UpdateStepCount;
1347 return actionStatePtr->lastPerformedInUpdate == currentUpdateStep && currentUpdateStep != default && actionStatePtr->frame == Time.frameCount;
1348 }
1349
1350 return false;
1351 }
1352
1353 /// <summary>
1354 /// Check whether <see cref="phase"/> transitioned from <see cref="InputActionPhase.Performed"/> to any other phase
1355 /// value at least once in the current frame.
1356 /// </summary>
1357 /// <returns>True if the action completed this frame.</returns>
1358 /// <remarks>
1359 /// Although <see cref="InputActionPhase.Disabled"/> is technically a phase, this method does not consider disabling
1360 /// the action while the action is in <see cref="InputActionPhase.Performed"/> to be "completed".
1361 ///
1362 /// This method is different from <see cref="WasReleasedThisFrame"/> in that it depends directly on the
1363 /// interaction(s) driving the action (including the default interaction if no specific interaction
1364 /// has been added to the action or binding).
1365 ///
1366 /// For example, let's say the action is bound to the space bar and that the binding has a
1367 /// <see cref="Interactions.HoldInteraction"/> assigned to it. In the frame where the space bar
1368 /// is pressed, <see cref="WasPressedThisFrame"/> will be true (because the button/key is now pressed)
1369 /// but <see cref="WasPerformedThisFrame"/> will still be false (because the hold has not been performed yet).
1370 /// If at that time the space bar is released, <see cref="WasReleasedThisFrame"/> will be true (because the
1371 /// button/key is now released) but <c>WasCompletedThisFrame</c> will still be false (because the hold
1372 /// had not been performed yet). If instead the space bar is held down for long enough for the hold interaction,
1373 /// the phase will change to and stay <see cref="InputActionPhase.Performed"/> and <see cref="WasPerformedThisFrame"/>
1374 /// will be true for one frame as it meets the duration threshold. Once released, <c>WasCompletedThisFrame</c> will be true
1375 /// (because the action is no longer performed) and only in the frame where the hold transitioned away from Performed.
1376 ///
1377 /// For another example where the action could be considered pressed but also completed, let's say the action
1378 /// is bound to the thumbstick and that the binding has a Sector interaction from the XR Interaction Toolkit assigned
1379 /// to it such that it only performs in the forward sector area past a button press threshold. In the frame where the
1380 /// thumbstick is pushed forward, both <see cref="WasPressedThisFrame"/> will be true (because the thumbstick actuation is
1381 /// now considered pressed) and <see cref="WasPerformedThisFrame"/> will be true (because the thumbstick is in
1382 /// the forward sector). If the thumbstick is then moved to the left in a sweeping motion, <see cref="IsPressed"/>
1383 /// will still be true. However, <c>WasCompletedThisFrame</c> will also be true (because the thumbstick is
1384 /// no longer in the forward sector while still crossed the button press threshold) and only in the frame where
1385 /// the thumbstick was no longer within the forward sector. For more details about the Sector interaction, see
1386 /// <a href="https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@2.5/api/UnityEngine.XR.Interaction.Toolkit.Inputs.Interactions.SectorInteraction.html"><c>SectorInteraction</c></a>
1387 /// in the XR Interaction Toolkit Scripting API documentation.
1388 /// <br />
1389 /// Unlike <see cref="ReadValue{TValue}"/>, which will reset when the action goes back to waiting
1390 /// state, this property will stay true for the duration of the current frame (that is, until the next
1391 /// <see cref="InputSystem.Update"/> runs) as long as the action was completed at least once.
1392 ///
1393 /// <example>
1394 /// <code>
1395 /// var teleport = playerInput.actions["Teleport"];
1396 /// if (teleport.WasPerformedThisFrame())
1397 /// InitiateTeleport();
1398 /// else if (teleport.WasCompletedThisFrame())
1399 /// StopTeleport();
1400 /// </code>
1401 /// </example>
1402 ///
1403 /// This method will disregard whether the action is currently enabled or disabled. It will keep returning
1404 /// true for the duration of the frame even if the action was subsequently disabled in the frame.
1405 ///
1406 /// The meaning of "frame" is either the current "dynamic" update (<c>MonoBehaviour.Update</c>) or the current
1407 /// fixed update (<c>MonoBehaviour.FixedUpdate</c>) depending on the value of the <see cref="InputSettings.updateMode"/> setting.
1408 /// </remarks>
1409 /// <seealso cref="WasPerformedThisFrame"/>
1410 /// <seealso cref="WasReleasedThisFrame"/>
1411 /// <seealso cref="phase"/>
1412 public unsafe bool WasCompletedThisFrame()
1413 {
1414 var state = GetOrCreateActionMap().m_State;
1415
1416 if (state != null)
1417 {
1418 var actionStatePtr = &state.actionStates[m_ActionIndexInState];
1419 var currentUpdateStep = InputUpdate.s_UpdateStepCount;
1420 return actionStatePtr->lastCompletedInUpdate == currentUpdateStep && currentUpdateStep != default && actionStatePtr->frame == Time.frameCount;
1421 }
1422
1423 return false;
1424 }
1425
1426 /// <summary>
1427 /// Return the completion percentage of the timeout (if any) running on the current interaction.
1428 /// </summary>
1429 /// <returns>A value >= 0 (no progress) and <= 1 (finished) indicating the level of completion
1430 /// of the currently running timeout.</returns>
1431 /// <remarks>
1432 /// This method is useful, for example, when providing UI feedback for an ongoing action. If, say,
1433 /// you have a <see cref="Interactions.HoldInteraction"/> on a binding, you might want to show a
1434 /// progress indicator in the UI and need to know how far into the hold the action
1435 /// current is. Once the hold has been started, this method will return how far into the hold
1436 /// the action currently is.
1437 ///
1438 /// Note that if an interaction performs and stays performed (see <see cref="InputInteractionContext.PerformedAndStayPerformed"/>),
1439 /// the completion percentage will remain at 1 until the interaction is canceled.
1440 ///
1441 /// Also note that completion is based on the progression of time and not dependent on input
1442 /// updates. This means that if, for example, the timeout for a <see cref="Interactions.HoldInteraction"/>
1443 /// has expired according the current time but the expiration has not yet been processed by
1444 /// an input update (thus causing the hold to perform), the returned completion percentage
1445 /// will still be 1. In other words, there isn't always a correlation between the current
1446 /// completion percentage and <see cref="phase"/>.
1447 ///
1448 /// The meaning of the timeout is dependent on the interaction in play. For a <see cref="Interactions.HoldInteraction"/>,
1449 /// "completion" represents the duration timeout (that is, the time until a "hold" is considered to be performed), whereas
1450 /// for a <see cref="Interactions.TapInteraction"/> "completion" represents "time to failure" (that is, the remaining time window
1451 /// that the interaction can be completed within).
1452 ///
1453 /// Note that an interaction might run multiple timeouts in succession. One such example is <see cref="Interactions.MultiTapInteraction"/>.
1454 /// In this case, progression towards a single timeout does not necessarily mean progression towards completion
1455 /// of the whole interaction. An interaction can call <see cref="InputInteractionContext.SetTotalTimeoutCompletionTime"/>
1456 /// to inform the Input System of the total length of timeouts to run. If this is done, the result of the
1457 /// <c>GetTimeoutCompletionPercentage</c> method will return a value reflecting the progression with respect
1458 /// to total time.
1459 ///
1460 /// <example>
1461 /// <code>
1462 /// // Scale a UI element in response to the completion of a hold on the gamepad's A button.
1463 ///
1464 /// Transform uiObjectToScale;
1465 ///
1466 /// InputAction holdAction;
1467 ///
1468 /// void OnEnable()
1469 /// {
1470 /// if (holdAction == null)
1471 /// {
1472 /// // Create hold action with a 2 second timeout.
1473 /// // NOTE: Here we create the action in code. You can, of course, grab the action from an .inputactions
1474 /// // asset created in the editor instead.
1475 /// holdAction = new InputAction(type: InputActionType.Button, interactions: "hold(duration=2)");
1476 ///
1477 /// // Show the UI object when the hold starts and hide it when it ends.
1478 /// holdAction.started += _ => uiObjectToScale.SetActive(true);
1479 /// holdAction.canceled += _ => uiObjectToScale.SetActive(false);
1480 ///
1481 /// // If you want to play a visual effect when the action performs, you can initiate from
1482 /// // the performed callback.
1483 /// holdAction.performed += _ => /* InitiateVisualEffectWhenHoldIsComplete() */;
1484 /// }
1485 ///
1486 /// holdAction.Enable();
1487 ///
1488 /// // Hide the UI object until the action is started.
1489 /// uiObjectToScale.gameObject.SetActive(false);
1490 /// }
1491 ///
1492 /// void OnDisable()
1493 /// {
1494 /// holdAction.Disable();
1495 /// }
1496 ///
1497 /// void Update()
1498 /// {
1499 /// var completion = holdAction.GetTimeoutCompletionPercentage();
1500 /// uiObjectToScale.localScale = new Vector3(1, completion, 1);
1501 /// }
1502 /// </code>
1503 /// </example>
1504 /// </remarks>
1505 /// <seealso cref="IInputInteraction"/>
1506 /// <seealso cref="InputInteractionContext.SetTimeout"/>
1507 /// <seealso cref="InputInteractionContext.SetTotalTimeoutCompletionTime"/>
1508 public unsafe float GetTimeoutCompletionPercentage()
1509 {
1510 var actionMap = GetOrCreateActionMap();
1511 var state = actionMap.m_State;
1512
1513 // If there's no state, there can't be activity on the action so our completion
1514 // percentage must be zero.
1515 if (state == null)
1516 return 0;
1517
1518 ref var actionState = ref state.actionStates[m_ActionIndexInState];
1519 var interactionIndex = actionState.interactionIndex;
1520 if (interactionIndex == -1)
1521 {
1522 ////REVIEW: should this use WasPerformedThisFrame()?
1523 // There's no interactions on the action or on the currently active binding, so go
1524 // entirely by the current phase. Performed is 100%, everything else is 0%.
1525 return actionState.phase == InputActionPhase.Performed ? 1 : 0;
1526 }
1527
1528 ref var interactionState = ref state.interactionStates[interactionIndex];
1529 switch (interactionState.phase)
1530 {
1531 case InputActionPhase.Started:
1532 // If the interaction was started and there is a timer running, the completion level
1533 // is determined by far we are between the interaction start time and timer expiration.
1534 var timerCompletion = 0f;
1535 if (interactionState.isTimerRunning)
1536 {
1537 var duration = interactionState.timerDuration;
1538 var startTime = interactionState.timerStartTime;
1539 var endTime = startTime + duration;
1540 var remainingTime = endTime - InputState.currentTime;
1541 if (remainingTime <= 0)
1542 timerCompletion = 1;
1543 else
1544 timerCompletion = (float)((duration - remainingTime) / duration);
1545 }
1546
1547 if (interactionState.totalTimeoutCompletionTimeRemaining > 0)
1548 {
1549 return (interactionState.totalTimeoutCompletionDone + timerCompletion * interactionState.timerDuration) /
1550 (interactionState.totalTimeoutCompletionDone + interactionState.totalTimeoutCompletionTimeRemaining);
1551 }
1552 else
1553 {
1554 return timerCompletion;
1555 }
1556
1557 case InputActionPhase.Performed:
1558 return 1;
1559 }
1560
1561 return 0;
1562 }
1563
1564 ////REVIEW: it would be best if these were InternedStrings; however, for serialization, it has to be strings
1565 [Tooltip("Human readable name of the action. Must be unique within its action map (case is ignored). Can be changed "
1566 + "without breaking references to the action.")]
1567 [SerializeField] internal string m_Name;
1568 [Tooltip("Determines how the action triggers.\n"
1569 + "\n"
1570 + "A Value action will start and perform when a control moves from its default value and then "
1571 + "perform on every value change. It will cancel when controls go back to default value. Also, when enabled, a Value "
1572 + "action will respond right away to a control's current value.\n"
1573 + "\n"
1574 + "A Button action will start when a button is pressed and perform when the press threshold (see 'Default Button Press Point' in settings) "
1575 + "is reached. It will cancel when the button is going below the release threshold (see 'Button Release Threshold' in settings). Also, "
1576 + "if a button is already pressed when the action is enabled, the button has to be released first.\n"
1577 + "\n"
1578 + "A Pass-Through action will not explicitly start and will never cancel. Instead, for every value change on any bound control, "
1579 + "the action will perform.")]
1580 [SerializeField] internal InputActionType m_Type;
1581 [FormerlySerializedAs("m_ExpectedControlLayout")]
1582 [Tooltip("The type of control expected by the action (e.g. \"Button\" or \"Stick\"). This will limit the controls shown "
1583 + "when setting up bindings in the UI and will also limit which controls can be bound interactively to the action.")]
1584 [SerializeField] internal string m_ExpectedControlType;
1585 [Tooltip("Unique ID of the action (GUID). Used to reference the action from bindings such that actions can be renamed "
1586 + "without breaking references.")]
1587 [SerializeField] internal string m_Id; // Can't serialize System.Guid and Unity's GUID is editor only.
1588 [SerializeField] internal string m_Processors;
1589 [SerializeField] internal string m_Interactions;
1590
1591 // For singleton actions, we serialize the bindings directly as part of the action.
1592 // For any other type of action, this is null.
1593 [SerializeField] internal InputBinding[] m_SingletonActionBindings;
1594 [SerializeField] internal ActionFlags m_Flags;
1595
1596 [NonSerialized] internal InputBinding? m_BindingMask;
1597 [NonSerialized] internal int m_BindingsStartIndex;
1598 [NonSerialized] internal int m_BindingsCount;
1599 [NonSerialized] internal int m_ControlStartIndex;
1600 [NonSerialized] internal int m_ControlCount;
1601
1602 /// <summary>
1603 /// Index of the action in the <see cref="InputActionState"/> associated with the
1604 /// action's <see cref="InputActionMap"/>.
1605 /// </summary>
1606 /// <remarks>
1607 /// This is not necessarily the same as the index of the action in its map.
1608 /// </remarks>
1609 /// <seealso cref="actionMap"/>
1610 [NonSerialized] internal int m_ActionIndexInState = InputActionState.kInvalidIndex;
1611
1612 /// <summary>
1613 /// The action map that owns the action.
1614 /// </summary>
1615 /// <remarks>
1616 /// This is not serialized. The action map will restore this back references after deserialization.
1617 /// </remarks>
1618 [NonSerialized] internal InputActionMap m_ActionMap;
1619
1620 // Listeners. No array allocations if only a single listener.
1621 [NonSerialized] internal CallbackArray<Action<CallbackContext>> m_OnStarted;
1622 [NonSerialized] internal CallbackArray<Action<CallbackContext>> m_OnCanceled;
1623 [NonSerialized] internal CallbackArray<Action<CallbackContext>> m_OnPerformed;
1624
1625 /// <summary>
1626 /// Whether the action is a loose action created in code (e.g. as a property on a component).
1627 /// </summary>
1628 /// <remarks>
1629 /// Singleton actions are not contained in maps visible to the user. Internally, we do create
1630 /// a map for them that contains just the singleton action. To the action system, there are no
1631 /// actions without action maps.
1632 /// </remarks>
1633 internal bool isSingletonAction => m_ActionMap == null || ReferenceEquals(m_ActionMap.m_SingletonAction, this);
1634
1635 [Flags]
1636 internal enum ActionFlags
1637 {
1638 WantsInitialStateCheck = 1 << 0,
1639 }
1640
1641 private InputActionState.TriggerState currentState
1642 {
1643 get
1644 {
1645 if (m_ActionIndexInState == InputActionState.kInvalidIndex)
1646 return new InputActionState.TriggerState();
1647 Debug.Assert(m_ActionMap != null, "Action must have associated action map");
1648 Debug.Assert(m_ActionMap.m_State != null, "Action map must have state at this point");
1649 return m_ActionMap.m_State.FetchActionState(this);
1650 }
1651 }
1652
1653 internal string MakeSureIdIsInPlace()
1654 {
1655 if (string.IsNullOrEmpty(m_Id))
1656 GenerateId();
1657 return m_Id;
1658 }
1659
1660 internal void GenerateId()
1661 {
1662 m_Id = Guid.NewGuid().ToString();
1663 }
1664
1665 internal InputActionMap GetOrCreateActionMap()
1666 {
1667 if (m_ActionMap == null)
1668 CreateInternalActionMapForSingletonAction();
1669 return m_ActionMap;
1670 }
1671
1672 private void CreateInternalActionMapForSingletonAction()
1673 {
1674 m_ActionMap = new InputActionMap
1675 {
1676 m_Actions = new[] { this },
1677 m_SingletonAction = this,
1678 m_Bindings = m_SingletonActionBindings
1679 };
1680 }
1681
1682 internal void RequestInitialStateCheckOnEnabledAction()
1683 {
1684 Debug.Assert(enabled, "This should only be called on actions that are enabled");
1685
1686 var map = GetOrCreateActionMap();
1687 var state = map.m_State;
1688 state.SetInitialStateCheckPending(m_ActionIndexInState);
1689 }
1690
1691 // NOTE: This does *NOT* check whether the control is valid according to the binding it
1692 // resolved from and/or the current binding mask. If, for example, the binding is
1693 // "<Keyboard>/#(ä)" and the keyboard switches from a DE layout to a US layout, the
1694 // key would still be considered valid even if the path in the binding would actually
1695 // no longer resolve to it.
1696 internal bool ActiveControlIsValid(InputControl control)
1697 {
1698 if (control == null)
1699 return false;
1700
1701 // Device must still be added.
1702 var device = control.device;
1703 if (!device.added)
1704 return false;
1705
1706 // If we have a device list in the map or asset, device
1707 // must be in list.
1708 var map = GetOrCreateActionMap();
1709 var deviceList = map.devices;
1710 if (deviceList != null && !deviceList.Value.ContainsReference(device))
1711 return false;
1712
1713 return true;
1714 }
1715
1716 internal InputBinding? FindEffectiveBindingMask()
1717 {
1718 if (m_BindingMask.HasValue)
1719 return m_BindingMask;
1720
1721 if (m_ActionMap?.m_BindingMask != null)
1722 return m_ActionMap.m_BindingMask;
1723
1724 return m_ActionMap?.m_Asset?.m_BindingMask;
1725 }
1726
1727 internal int BindingIndexOnActionToBindingIndexOnMap(int indexOfBindingOnAction)
1728 {
1729 // We don't want to hit InputAction.bindings here as this requires setting up per-action
1730 // binding info which we then nuke as part of the override process. Calling ApplyBindingOverride
1731 // repeatedly with an index would thus cause the same data to be computed and thrown away
1732 // over and over.
1733 // Instead we manually search through the map's bindings to find the right binding index
1734 // in the map.
1735
1736 var actionMap = GetOrCreateActionMap();
1737 var bindingsInMap = actionMap.m_Bindings;
1738 var bindingCountInMap = bindingsInMap.LengthSafe();
1739 var actionName = name;
1740
1741 var currentBindingIndexOnAction = -1;
1742 for (var i = 0; i < bindingCountInMap; ++i)
1743 {
1744 ref var binding = ref bindingsInMap[i];
1745
1746 if (!binding.TriggersAction(this))
1747 continue;
1748
1749 ++currentBindingIndexOnAction;
1750 if (currentBindingIndexOnAction == indexOfBindingOnAction)
1751 return i;
1752 }
1753
1754 throw new ArgumentOutOfRangeException(nameof(indexOfBindingOnAction),
1755 $"Binding index {indexOfBindingOnAction} is out of range for action '{this}' with {currentBindingIndexOnAction + 1} bindings");
1756 }
1757
1758 internal int BindingIndexOnMapToBindingIndexOnAction(int indexOfBindingOnMap)
1759 {
1760 var actionMap = GetOrCreateActionMap();
1761 var bindingsInMap = actionMap.m_Bindings;
1762 var actionName = name;
1763
1764 var bindingIndexOnAction = 0;
1765 for (var i = indexOfBindingOnMap - 1; i >= 0; --i)
1766 {
1767 ref var binding = ref bindingsInMap[i];
1768
1769 if (string.Compare(binding.action, actionName, StringComparison.InvariantCultureIgnoreCase) == 0 ||
1770 binding.action == m_Id)
1771 ++bindingIndexOnAction;
1772 }
1773
1774 return bindingIndexOnAction;
1775 }
1776
1777 ////TODO: make current event available in some form
1778 ////TODO: make source binding info available (binding index? binding instance?)
1779
1780 /// <summary>
1781 /// Information provided to action callbacks about what triggered an action.
1782 /// </summary>
1783 /// <remarks>
1784 /// This struct should not be held on to past the duration of the callback.
1785 /// </remarks>
1786 /// <seealso cref="performed"/>
1787 /// <seealso cref="started"/>
1788 /// <seealso cref="canceled"/>
1789 /// <seealso cref="InputActionMap.actionTriggered"/>
1790 public struct CallbackContext // Ideally would be a ref struct but couldn't use it in lambdas then.
1791 {
1792 internal InputActionState m_State;
1793 internal int m_ActionIndex;
1794
1795 ////REVIEW: there should probably be a mechanism for the user to be able to correlate
1796 //// the callback to a specific binding on the action
1797
1798 private int actionIndex => m_ActionIndex;
1799 private unsafe int bindingIndex => m_State.actionStates[actionIndex].bindingIndex;
1800 private unsafe int controlIndex => m_State.actionStates[actionIndex].controlIndex;
1801 private unsafe int interactionIndex => m_State.actionStates[actionIndex].interactionIndex;
1802
1803 /// <summary>
1804 /// Current phase of the action. Equivalent to accessing <see cref="InputAction.phase"/>
1805 /// on <see cref="action"/>.
1806 /// </summary>
1807 /// <value>Current phase of the action.</value>
1808 /// <seealso cref="started"/>
1809 /// <seealso cref="performed"/>
1810 /// <seealso cref="canceled"/>
1811 /// <seealso cref="InputAction.phase"/>
1812 public unsafe InputActionPhase phase
1813 {
1814 get
1815 {
1816 if (m_State == null)
1817 return InputActionPhase.Disabled;
1818 return m_State.actionStates[actionIndex].phase;
1819 }
1820 }
1821
1822 /// <summary>
1823 /// Whether the <see cref="action"/> has just been started.
1824 /// </summary>
1825 /// <value>If true, the action was just started.</value>
1826 /// <seealso cref="InputAction.started"/>
1827 public bool started => phase == InputActionPhase.Started;
1828
1829 /// <summary>
1830 /// Whether the <see cref="action"/> has just been performed.
1831 /// </summary>
1832 /// <value>If true, the action was just performed.</value>
1833 /// <seealso cref="InputAction.performed"/>
1834 public bool performed => phase == InputActionPhase.Performed;
1835
1836 /// <summary>
1837 /// Whether the <see cref="action"/> has just been canceled.
1838 /// </summary>
1839 /// <value>If true, the action was just canceled.</value>
1840 /// <seealso cref="InputAction.canceled"/>
1841 public bool canceled => phase == InputActionPhase.Canceled;
1842
1843 /// <summary>
1844 /// The action that got triggered.
1845 /// </summary>
1846 /// <value>Action that got triggered.</value>
1847 public InputAction action => m_State?.GetActionOrNull(bindingIndex);
1848
1849 /// <summary>
1850 /// The control that triggered the action.
1851 /// </summary>
1852 /// <value>Control that triggered the action.</value>
1853 /// <remarks>
1854 /// In case of a composite binding, this is the control of the composite that activated the
1855 /// composite as a whole. For example, in case of a WASD-style binding, it could be the W key.
1856 ///
1857 /// Note that an action may also change its <see cref="phase"/> in response to a timeout.
1858 /// For example, a <see cref="Interactions.TapInteraction"/> will cancel itself if the
1859 /// button control is not released within a certain time. When this happens, the <c>control</c>
1860 /// property will be the control that last fed input into the action.
1861 /// </remarks>
1862 /// <seealso cref="InputAction.controls"/>
1863 /// <seealso cref="InputBinding.path"/>
1864 public InputControl control => m_State?.controls[controlIndex];
1865
1866 /// <summary>
1867 /// The interaction that triggered the action or <c>null</c> if the binding that triggered does not
1868 /// have any particular interaction set on it.
1869 /// </summary>
1870 /// <value>Interaction that triggered the callback.</value>
1871 /// <remarks>
1872 /// <example>
1873 /// <code>
1874 /// void FirePerformed(InputAction.CallbackContext context)
1875 /// {
1876 /// // If SlowTap interaction was performed, perform a charged
1877 /// // firing. Otherwise, fire normally.
1878 /// if (context.interaction is SlowTapInteraction)
1879 /// FireChargedProjectile();
1880 /// else
1881 /// FireNormalProjectile();
1882 /// }
1883 /// </code>
1884 /// </example>
1885 /// </remarks>
1886 /// <seealso cref="InputBinding.interactions"/>
1887 /// <seealso cref="InputAction.interactions"/>
1888 public IInputInteraction interaction
1889 {
1890 get
1891 {
1892 if (m_State == null)
1893 return null;
1894 var index = interactionIndex;
1895 if (index == InputActionState.kInvalidIndex)
1896 return null;
1897 return m_State.interactions[index];
1898 }
1899 }
1900
1901 /// <summary>
1902 /// The time at which the action got triggered.
1903 /// </summary>
1904 /// <value>Time relative to <c>Time.realtimeSinceStartup</c> at which
1905 /// the action got triggered.</value>
1906 /// <remarks>
1907 /// This is usually determined by the timestamp of the input event that activated a control
1908 /// bound to the action. What this means is that this is normally <em>not</em> the
1909 /// value of <c>Time.realtimeSinceStartup</c> when the input system calls the
1910 /// callback but rather the time at which the input was generated that triggered
1911 /// the action.
1912 /// </remarks>
1913 /// <seealso cref="InputEvent.time"/>
1914 public unsafe double time
1915 {
1916 get
1917 {
1918 if (m_State == null)
1919 return 0;
1920 return m_State.actionStates[actionIndex].time;
1921 }
1922 }
1923
1924 /// <summary>
1925 /// Time at which the action was started.
1926 /// </summary>
1927 /// <value>Value relative to <c>Time.realtimeSinceStartup</c> when the action
1928 /// changed to <see cref="started"/>.</value>
1929 /// <remarks>
1930 /// This is only relevant for actions that go through distinct a <see cref="InputActionPhase.Started"/>
1931 /// cycle as driven by <see cref="IInputInteraction">interactions</see>.
1932 ///
1933 /// The value of this property is that of <see cref="time"/> when <see
1934 /// cref="InputAction.started"/> was called. See the <see cref="time"/>
1935 /// property for how the timestamp works.
1936 /// </remarks>
1937 public unsafe double startTime
1938 {
1939 get
1940 {
1941 if (m_State == null)
1942 return 0;
1943 return m_State.actionStates[actionIndex].startTime;
1944 }
1945 }
1946
1947 /// <summary>
1948 /// Time difference between <see cref="time"/> and <see cref="startTime"/>.
1949 /// </summary>
1950 /// <value>Difference between <see cref="time"/> and <see cref="startTime"/>.</value>
1951 /// <remarks>
1952 /// This property can be used, for example, to determine how long a button
1953 /// was held down.
1954 ///
1955 /// <example>
1956 /// <code>
1957 /// // Let's create a button action bound to the A button
1958 /// // on the gamepad.
1959 /// var action = new InputAction(
1960 /// type: InputActionType.Button,
1961 /// binding: "<Gamepad>/buttonSouth");
1962 ///
1963 /// // When the action is performed (which will happen when the
1964 /// // button is pressed and then released) we take the duration
1965 /// // of the press to determine how many projectiles to spawn.
1966 /// action.performed +=
1967 /// context =>
1968 /// {
1969 /// const float kSpawnRate = 3; // 3 projectiles per second
1970 /// var projectileCount = kSpawnRate * context.duration;
1971 /// for (var i = 0; i < projectileCount; ++i)
1972 /// {
1973 /// var projectile = UnityEngine.Object.Instantiate(projectile);
1974 /// // Apply other changes to the projectile...
1975 /// }
1976 /// };
1977 /// </code>
1978 /// </example>
1979 /// </remarks>
1980 public double duration => time - startTime;
1981
1982 /// <summary>
1983 /// Type of value returned by <see cref="ReadValueAsObject"/> and expected
1984 /// by <see cref="ReadValue{TValue}"/>.
1985 /// </summary>
1986 /// <value>Type of object returned when reading a value.</value>
1987 /// <remarks>
1988 /// The type of value returned by an action is usually determined by the
1989 /// <see cref="InputControl"/> that triggered the action, i.e. by the
1990 /// control referenced from <see cref="control"/>.
1991 ///
1992 /// However, if the binding that triggered is a composite, then the composite
1993 /// will determine values and not the individual control that triggered (that
1994 /// one just feeds values into the composite).
1995 /// </remarks>
1996 /// <seealso cref="InputControl.valueType"/>
1997 /// <seealso cref="InputBindingComposite.valueType"/>
1998 /// <seealso cref="activeValueType"/>
1999 public Type valueType => m_State?.GetValueType(bindingIndex, controlIndex);
2000
2001 /// <summary>
2002 /// Size of values returned by <see cref="ReadValue(void*,int)"/>.
2003 /// </summary>
2004 /// <value>Size of value returned when reading.</value>
2005 /// <remarks>
2006 /// All input values passed around by the system are required to be "blittable",
2007 /// i.e. they cannot contain references, cannot be heap objects themselves, and
2008 /// must be trivially mem-copyable. This means that any value can be read out
2009 /// and retained in a raw byte buffer.
2010 ///
2011 /// The value of this property determines how many bytes will be written
2012 /// by <see cref="ReadValue(void*,int)"/>.
2013 /// </remarks>
2014 /// <seealso cref="InputControl.valueSizeInBytes"/>
2015 /// <seealso cref="InputBindingComposite.valueSizeInBytes"/>
2016 /// <seealso cref="ReadValue(void*,int)"/>
2017 public int valueSizeInBytes
2018 {
2019 get
2020 {
2021 if (m_State == null)
2022 return 0;
2023
2024 return m_State.GetValueSizeInBytes(bindingIndex, controlIndex);
2025 }
2026 }
2027
2028 /// <summary>
2029 /// Read the value of the action as a raw byte buffer. This allows reading
2030 /// values without having to know value types but also, unlike <see cref="ReadValueAsObject"/>,
2031 /// without allocating GC heap memory.
2032 /// </summary>
2033 /// <param name="buffer">Memory buffer to read the value into.</param>
2034 /// <param name="bufferSize">Size of buffer allocated at <paramref name="buffer"/>. Must be
2035 /// at least <see cref="valueSizeInBytes"/>.</param>
2036 /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <c>null</c>.</exception>
2037 /// <exception cref="ArgumentException"><paramref name="bufferSize"/> is too small.</exception>
2038 /// <remarks>
2039 /// <example>
2040 /// <code>
2041 /// // Read a Vector2 using the raw memory ReadValue API.
2042 /// // Here we just read into a local variable which we could
2043 /// // just as well (and more easily) do using ReadValue<Vector2>.
2044 /// // Still, it serves as a demonstration for how the API
2045 /// // operates in general.
2046 /// unsafe
2047 /// {
2048 /// var value = default(Vector2);
2049 /// var valuePtr = UnsafeUtility.AddressOf(ref value);
2050 /// context.ReadValue(buffer, UnsafeUtility.SizeOf<Vector2>());
2051 /// }
2052 /// </code>
2053 /// </example>
2054 /// </remarks>
2055 /// <seealso cref="InputControlExtensions.ReadValueIntoBuffer"/>
2056 /// <seealso cref="InputAction.ReadValue{TValue}"/>
2057 /// <seealso cref="ReadValue{TValue}"/>
2058 public unsafe void ReadValue(void* buffer, int bufferSize)
2059 {
2060 if (buffer == null)
2061 throw new ArgumentNullException(nameof(buffer));
2062
2063 if (m_State != null && phase.IsInProgress())
2064 {
2065 m_State.ReadValue(bindingIndex, controlIndex, buffer, bufferSize);
2066 }
2067 else
2068 {
2069 var valueSize = valueSizeInBytes;
2070 if (bufferSize < valueSize)
2071 throw new ArgumentException(
2072 $"Expected buffer of at least {valueSize} bytes but got buffer of only {bufferSize} bytes", nameof(bufferSize));
2073 UnsafeUtility.MemClear(buffer, valueSizeInBytes);
2074 }
2075 }
2076
2077 /// <summary>
2078 /// Read the value of the action.
2079 /// </summary>
2080 /// <typeparam name="TValue">Type of value to read. This must correspond to the
2081 /// expected by either <see cref="control"/> or, if it is a composite, by the
2082 /// <see cref="InputBindingComposite"/> in use.</typeparam>
2083 /// <returns>The value read from the action.</returns>
2084 /// <exception cref="InvalidOperationException">The given type <typeparamref name="TValue"/>
2085 /// does not match the value type expected by the control or binding composite.</exception>
2086 /// <seealso cref="InputAction.ReadValue{TValue}"/>
2087 /// <seealso cref="ReadValue(void*,int)"/>
2088 /// <seealso cref="ReadValueAsObject"/>
2089 public TValue ReadValue<TValue>()
2090 where TValue : struct
2091 {
2092 var value = default(TValue);
2093 if (m_State != null)
2094 {
2095 value = phase.IsInProgress() ?
2096 m_State.ReadValue<TValue>(bindingIndex, controlIndex) :
2097 m_State.ApplyProcessors(bindingIndex, value);
2098 }
2099
2100 return value;
2101 }
2102
2103 /// <summary>
2104 /// Read the current value of the action as a <c>float</c> and return true if it is equal to
2105 /// or greater than the button press threshold.
2106 /// </summary>
2107 /// <returns>True if the action is considered in "pressed" state, false otherwise.</returns>
2108 /// <remarks>
2109 /// If the currently active control is a <see cref="ButtonControl"/>, the <see cref="ButtonControl.pressPoint"/>
2110 /// of the button will be taken into account (if set). If there is no custom button press point, the
2111 /// global <see cref="InputSettings.defaultButtonPressPoint"/> will be used.
2112 /// </remarks>
2113 /// <seealso cref="InputSettings.defaultButtonPressPoint"/>
2114 /// <seealso cref="ButtonControl.pressPoint"/>
2115 public bool ReadValueAsButton()
2116 {
2117 var value = false;
2118 if (m_State != null && phase.IsInProgress())
2119 value = m_State.ReadValueAsButton(bindingIndex, controlIndex);
2120 return value;
2121 }
2122
2123 /// <summary>
2124 /// Same as <see cref="ReadValue{TValue}"/> except that it is not necessary to
2125 /// know the type of value at compile time.
2126 /// </summary>
2127 /// <returns>The current value from the binding that triggered the action or <c>null</c> if the action
2128 /// is not currently in progress.</returns>
2129 /// <remarks>
2130 /// This method allocates GC heap memory. Using it during normal gameplay will lead
2131 /// to frame-rate instabilities.
2132 /// </remarks>
2133 /// <seealso cref="ReadValue{TValue}"/>
2134 /// <seealso cref="InputAction.ReadValueAsObject"/>
2135 public object ReadValueAsObject()
2136 {
2137 if (m_State != null && phase.IsInProgress())
2138 return m_State.ReadValueAsObject(bindingIndex, controlIndex);
2139 return null;
2140 }
2141
2142 /// <summary>
2143 /// Return a string representation of the context useful for debugging.
2144 /// </summary>
2145 /// <returns>String representation of the context.</returns>
2146 public override string ToString()
2147 {
2148 return $"{{ action={action} phase={phase} time={time} control={control} value={ReadValueAsObject()} interaction={interaction} }}";
2149 }
2150 }
2151 }
2152}