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: "&lt;Gamepad&gt;/buttonSouth"); 67 /// 68 /// // Additional bindings can be added using `AddBinding`. 69 /// action.AddBinding("&lt;Mouse&gt;/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&lt;Vector2&gt;(); 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("&lt;Gamepad&gt;/leftStick"); 259 /// action.AddBinding("&lt;Joystick&gt;/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("&lt;Gamepad&gt;/buttonSouth"); 286 /// action.AddBinding("&lt;Joystick&gt;/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("&lt;Gamepad&gt;/buttonSouth", groups: "Gamepad"); 348 /// action.AddBinding("&lt;Keyboard&gt;/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 = "&lt;Keyboard&gt;/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("&lt;Gamepad&gt;/buttonSouth"); 450 /// action1.AddBinding("&lt;Gamepad&gt;/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("&lt;Gamepad&gt;/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("&lt;Gamepad&gt;/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("&lt;Gamepad&gt;/leftStick"); 703 /// action.AddBinding("&lt;Mouse&gt;/delta"); 704 /// 705 /// action.performed += ctx => Debug.Log("Value: " + ctx.ReadValue&lt;Vector2&gt;()); 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: "&lt;Gamepad&gt;/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&lt;Gamepad&gt;(); 878 /// 879 /// var action = new InputAction(type: InputActionType.Value, binding: "&lt;Gamepad&gt;/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&lt;Vector2&gt;() * (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() &amp;&amp; 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() &amp;&amp; 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 &gt;= 0 (no progress) and &lt;= 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 += _ =&gt; uiObjectToScale.SetActive(true); 1479 /// holdAction.canceled += _ =&gt; 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 += _ =&gt; /* 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: "&lt;Gamepad&gt;/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 &lt; 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&lt;Vector2&gt;. 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&lt;Vector2&gt;()); 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}