A game about forced loneliness, made by TACStudios
1#if PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI 2using System; 3using UnityEngine.InputSystem.LowLevel; 4using UnityEngine.UI; 5 6#if UNITY_EDITOR 7using UnityEngine.InputSystem.Editor; 8#endif 9 10////TODO: respect cursor lock mode 11 12////TODO: investigate how driving the HW cursor behaves when FPS drops low 13//// (also, maybe we can add support where we turn the gamepad mouse on and off automatically based on whether the system mouse is used) 14 15////TODO: add support for acceleration 16 17////TODO: automatically scale mouse speed to resolution such that it stays constant regardless of resolution 18 19////TODO: make it work with PlayerInput such that it will automatically look up actions in the actual PlayerInput instance it is used with (based on the action IDs it has) 20 21////REVIEW: should we default the SW cursor position to the center of the screen? 22 23////REVIEW: consider this for inclusion directly in the input system 24 25namespace UnityEngine.InputSystem.UI 26{ 27 /// <summary> 28 /// A component that creates a virtual <see cref="Mouse"/> device and drives its input from gamepad-style inputs. This effectively 29 /// adds a software mouse cursor. 30 /// </summary> 31 /// <remarks> 32 /// This component can be used with UIs that are designed for mouse input, i.e. need to be operated with a cursor. 33 /// By hooking up the <see cref="InputAction"/>s of this component to gamepad input and directing <see cref="cursorTransform"/> 34 /// to the UI transform of the cursor, you can use this component to drive an on-screen cursor. 35 /// 36 /// Note that this component does not actually trigger UI input itself. Instead, it creates a virtual <see cref="Mouse"/> 37 /// device which can then be picked up elsewhere (such as by <see cref="InputSystemUIInputModule"/>) where mouse/pointer input 38 /// is expected. 39 /// 40 /// Also note that if there is a <see cref="Mouse"/> added by the platform, it is not impacted by this component. More specifically, 41 /// the system mouse cursor will not be moved or otherwise used by this component. 42 /// 43 /// Input from the component is visible in the same frame as the source input on its actions by virtue of using <see cref="InputState.Change"/>. 44 /// </remarks> 45 /// <seealso cref="Gamepad"/> 46 /// <seealso cref="Mouse"/> 47 [AddComponentMenu("Input/Virtual Mouse")] 48 [HelpURL(InputSystem.kDocUrl + "/manual/UISupport.html#virtual-mouse-cursor-control")] 49 public class VirtualMouseInput : MonoBehaviour 50 { 51 /// <summary> 52 /// Optional transform that will be updated to correspond to the current mouse position. 53 /// </summary> 54 /// <value>Transform to update with mouse position.</value> 55 /// <remarks> 56 /// This is useful for having a UI object that directly represents the mouse cursor. Simply add both the 57 /// <c>VirtualMouseInput</c> component and an <a href="https://docs.unity3d.com/Manual/script-Image.html">Image</a> 58 /// component and hook the <a href="https://docs.unity3d.com/ScriptReference/RectTransform.html">RectTransform</a> 59 /// component for the UI object into here. The object as a whole will then follow the generated mouse cursor 60 /// motion. 61 /// </remarks> 62 public RectTransform cursorTransform 63 { 64 get => m_CursorTransform; 65 set => m_CursorTransform = value; 66 } 67 68 /// <summary> 69 /// How many pixels per second the cursor travels in one axis when the respective axis from 70 /// <see cref="stickAction"/> is 1. 71 /// </summary> 72 /// <value>Mouse speed in pixels per second.</value> 73 public float cursorSpeed 74 { 75 get => m_CursorSpeed; 76 set => m_CursorSpeed = value; 77 } 78 79 /// <summary> 80 /// Determines which cursor representation to use. If this is set to <see cref="CursorMode.SoftwareCursor"/> 81 /// (the default), then <see cref="cursorGraphic"/> and <see cref="cursorTransform"/> define a software cursor 82 /// that is made to correspond to the position of <see cref="virtualMouse"/>. If this is set to <see 83 /// cref="CursorMode.HardwareCursorIfAvailable"/> and there is a native <see cref="Mouse"/> device present, 84 /// the component will take over that mouse device and disable it (so as for it to not also generate position 85 /// updates). It will then use <see cref="Mouse.WarpCursorPosition"/> to move the system mouse cursor to 86 /// correspond to the position of the <see cref="virtualMouse"/>. In this case, <see cref="cursorGraphic"/> 87 /// will be disabled and <see cref="cursorTransform"/> will not be updated. 88 /// </summary> 89 /// <value>Whether the system mouse cursor (if present) should be made to correspond with the virtual mouse position.</value> 90 /// <remarks> 91 /// Note that regardless of which mode is used for the cursor, mouse input is expected to be picked up from <see cref="virtualMouse"/>. 92 /// 93 /// Note that if <see cref="CursorMode.HardwareCursorIfAvailable"/> is used, the software cursor is still used 94 /// if no native <see cref="Mouse"/> device is present. 95 /// </remarks> 96 public CursorMode cursorMode 97 { 98 get => m_CursorMode; 99 set 100 { 101 if (m_CursorMode == value) 102 return; 103 104 // If we're turning it off, make sure we re-enable the system mouse. 105 if (m_CursorMode == CursorMode.HardwareCursorIfAvailable && m_SystemMouse != null) 106 { 107 InputSystem.EnableDevice(m_SystemMouse); 108 m_SystemMouse = null; 109 } 110 111 m_CursorMode = value; 112 113 if (m_CursorMode == CursorMode.HardwareCursorIfAvailable) 114 TryEnableHardwareCursor(); 115 else if (m_CursorGraphic != null) 116 m_CursorGraphic.enabled = true; 117 } 118 } 119 120 /// <summary> 121 /// The UI graphic element that represents the mouse cursor. 122 /// </summary> 123 /// <value>Graphic element for the software mouse cursor.</value> 124 /// <remarks> 125 /// If <see cref="cursorMode"/> is set to <see cref="CursorMode.HardwareCursorIfAvailable"/>, this graphic will 126 /// be disabled. 127 /// 128 /// Also, this UI component implicitly determines the <c>Canvas</c> that defines the screen area for the cursor. 129 /// The canvas that this graphic is on will be looked up using <c>GetComponentInParent</c> and then the <c>Canvas.pixelRect</c> 130 /// of the canvas is used as the bounds for the cursor motion range. 131 /// </remarks> 132 /// <seealso cref="CursorMode.SoftwareCursor"/> 133 public Graphic cursorGraphic 134 { 135 get => m_CursorGraphic; 136 set 137 { 138 m_CursorGraphic = value; 139 TryFindCanvas(); 140 } 141 } 142 143 /// <summary> 144 /// Multiplier for values received from <see cref="scrollWheelAction"/>. 145 /// </summary> 146 /// <value>Multiplier for scroll values.</value> 147 public float scrollSpeed 148 { 149 get => m_ScrollSpeed; 150 set => m_ScrollSpeed = value; 151 } 152 153 /// <summary> 154 /// The virtual mouse device that the component feeds with input. 155 /// </summary> 156 /// <value>Instance of virtual mouse or <c>null</c>.</value> 157 /// <remarks> 158 /// This is only initialized after the component has been enabled for the first time. Note that 159 /// when subsequently disabling the component, the property will continue to return the mouse device 160 /// but the device will not be added to the system while the component is not enabled. 161 /// </remarks> 162 public Mouse virtualMouse => m_VirtualMouse; 163 164 /// <summary> 165 /// The Vector2 stick input that drives the mouse cursor, i.e. <see cref="Pointer.position"/> on 166 /// <see cref="virtualMouse"/> and the <a 167 /// href="https://docs.unity3d.com/ScriptReference/RectTransform-anchoredPosition.html">anchoredPosition</a> 168 /// on <see cref="cursorTransform"/> (if set). 169 /// </summary> 170 /// <value>Stick input that drives cursor position.</value> 171 /// <remarks> 172 /// This should normally be bound to controls such as <see cref="Gamepad.leftStick"/> and/or 173 /// <see cref="Gamepad.rightStick"/>. 174 /// </remarks> 175 public InputActionProperty stickAction 176 { 177 get => m_StickAction; 178 set => SetAction(ref m_StickAction, value); 179 } 180 181 /// <summary> 182 /// Optional button input that determines when <see cref="Mouse.leftButton"/> is pressed on 183 /// <see cref="virtualMouse"/>. 184 /// </summary> 185 /// <value>Input for <see cref="Mouse.leftButton"/>.</value> 186 public InputActionProperty leftButtonAction 187 { 188 get => m_LeftButtonAction; 189 set 190 { 191 if (m_ButtonActionTriggeredDelegate != null) 192 SetActionCallback(m_LeftButtonAction, m_ButtonActionTriggeredDelegate, false); 193 SetAction(ref m_LeftButtonAction, value); 194 if (m_ButtonActionTriggeredDelegate != null) 195 SetActionCallback(m_LeftButtonAction, m_ButtonActionTriggeredDelegate, true); 196 } 197 } 198 199 /// <summary> 200 /// Optional button input that determines when <see cref="Mouse.rightButton"/> is pressed on 201 /// <see cref="virtualMouse"/>. 202 /// </summary> 203 /// <value>Input for <see cref="Mouse.rightButton"/>.</value> 204 public InputActionProperty rightButtonAction 205 { 206 get => m_RightButtonAction; 207 set 208 { 209 if (m_ButtonActionTriggeredDelegate != null) 210 SetActionCallback(m_RightButtonAction, m_ButtonActionTriggeredDelegate, false); 211 SetAction(ref m_RightButtonAction, value); 212 if (m_ButtonActionTriggeredDelegate != null) 213 SetActionCallback(m_RightButtonAction, m_ButtonActionTriggeredDelegate, true); 214 } 215 } 216 217 /// <summary> 218 /// Optional button input that determines when <see cref="Mouse.middleButton"/> is pressed on 219 /// <see cref="virtualMouse"/>. 220 /// </summary> 221 /// <value>Input for <see cref="Mouse.middleButton"/>.</value> 222 public InputActionProperty middleButtonAction 223 { 224 get => m_MiddleButtonAction; 225 set 226 { 227 if (m_ButtonActionTriggeredDelegate != null) 228 SetActionCallback(m_MiddleButtonAction, m_ButtonActionTriggeredDelegate, false); 229 SetAction(ref m_MiddleButtonAction, value); 230 if (m_ButtonActionTriggeredDelegate != null) 231 SetActionCallback(m_MiddleButtonAction, m_ButtonActionTriggeredDelegate, true); 232 } 233 } 234 235 /// <summary> 236 /// Optional button input that determines when <see cref="Mouse.forwardButton"/> is pressed on 237 /// <see cref="virtualMouse"/>. 238 /// </summary> 239 /// <value>Input for <see cref="Mouse.forwardButton"/>.</value> 240 public InputActionProperty forwardButtonAction 241 { 242 get => m_ForwardButtonAction; 243 set 244 { 245 if (m_ButtonActionTriggeredDelegate != null) 246 SetActionCallback(m_ForwardButtonAction, m_ButtonActionTriggeredDelegate, false); 247 SetAction(ref m_ForwardButtonAction, value); 248 if (m_ButtonActionTriggeredDelegate != null) 249 SetActionCallback(m_ForwardButtonAction, m_ButtonActionTriggeredDelegate, true); 250 } 251 } 252 253 /// <summary> 254 /// Optional button input that determines when <see cref="Mouse.forwardButton"/> is pressed on 255 /// <see cref="virtualMouse"/>. 256 /// </summary> 257 /// <value>Input for <see cref="Mouse.forwardButton"/>.</value> 258 public InputActionProperty backButtonAction 259 { 260 get => m_BackButtonAction; 261 set 262 { 263 if (m_ButtonActionTriggeredDelegate != null) 264 SetActionCallback(m_BackButtonAction, m_ButtonActionTriggeredDelegate, false); 265 SetAction(ref m_BackButtonAction, value); 266 if (m_ButtonActionTriggeredDelegate != null) 267 SetActionCallback(m_BackButtonAction, m_ButtonActionTriggeredDelegate, true); 268 } 269 } 270 271 /// <summary> 272 /// Optional Vector2 value input that determines the value of <see cref="Mouse.scroll"/> on 273 /// <see cref="virtualMouse"/>. 274 /// </summary> 275 /// <value>Input for <see cref="Mouse.scroll"/>.</value> 276 /// <remarks> 277 /// In case you want to only bind vertical scrolling, simply have a <see cref="Composites.Vector2Composite"/> 278 /// with only <c>Up</c> and <c>Down</c> bound and <c>Left</c> and <c>Right</c> deleted or bound to nothing. 279 /// </remarks> 280 public InputActionProperty scrollWheelAction 281 { 282 get => m_ScrollWheelAction; 283 set => SetAction(ref m_ScrollWheelAction, value); 284 } 285 286 protected void OnEnable() 287 { 288 // Hijack system mouse, if enabled. 289 if (m_CursorMode == CursorMode.HardwareCursorIfAvailable) 290 TryEnableHardwareCursor(); 291 292 // Add mouse device. 293 if (m_VirtualMouse == null) 294 m_VirtualMouse = (Mouse)InputSystem.AddDevice("VirtualMouse"); 295 else if (!m_VirtualMouse.added) 296 InputSystem.AddDevice(m_VirtualMouse); 297 298 // Set initial cursor position. 299 if (m_CursorTransform != null) 300 { 301 var position = m_CursorTransform.anchoredPosition; 302 InputState.Change(m_VirtualMouse.position, position); 303 m_SystemMouse?.WarpCursorPosition(position); 304 } 305 306 // Hook into input update. 307 if (m_AfterInputUpdateDelegate == null) 308 m_AfterInputUpdateDelegate = OnAfterInputUpdate; 309 InputSystem.onAfterUpdate += m_AfterInputUpdateDelegate; 310 311 // Hook into actions. 312 if (m_ButtonActionTriggeredDelegate == null) 313 m_ButtonActionTriggeredDelegate = OnButtonActionTriggered; 314 SetActionCallback(m_LeftButtonAction, m_ButtonActionTriggeredDelegate, true); 315 SetActionCallback(m_RightButtonAction, m_ButtonActionTriggeredDelegate, true); 316 SetActionCallback(m_MiddleButtonAction, m_ButtonActionTriggeredDelegate, true); 317 SetActionCallback(m_ForwardButtonAction, m_ButtonActionTriggeredDelegate, true); 318 SetActionCallback(m_BackButtonAction, m_ButtonActionTriggeredDelegate, true); 319 320 // Enable actions. 321 m_StickAction.action?.Enable(); 322 m_LeftButtonAction.action?.Enable(); 323 m_RightButtonAction.action?.Enable(); 324 m_MiddleButtonAction.action?.Enable(); 325 m_ForwardButtonAction.action?.Enable(); 326 m_BackButtonAction.action?.Enable(); 327 m_ScrollWheelAction.action?.Enable(); 328 } 329 330 protected void OnDisable() 331 { 332 // Remove mouse device. 333 if (m_VirtualMouse != null && m_VirtualMouse.added) 334 InputSystem.RemoveDevice(m_VirtualMouse); 335 336 // Let go of system mouse. 337 if (m_SystemMouse != null) 338 { 339 InputSystem.EnableDevice(m_SystemMouse); 340 m_SystemMouse = null; 341 } 342 343 // Remove ourselves from input update. 344 if (m_AfterInputUpdateDelegate != null) 345 InputSystem.onAfterUpdate -= m_AfterInputUpdateDelegate; 346 347 // Disable actions. 348 m_StickAction.action?.Disable(); 349 m_LeftButtonAction.action?.Disable(); 350 m_RightButtonAction.action?.Disable(); 351 m_MiddleButtonAction.action?.Disable(); 352 m_ForwardButtonAction.action?.Disable(); 353 m_BackButtonAction.action?.Disable(); 354 m_ScrollWheelAction.action?.Disable(); 355 356 // Unhock from actions. 357 if (m_ButtonActionTriggeredDelegate != null) 358 { 359 SetActionCallback(m_LeftButtonAction, m_ButtonActionTriggeredDelegate, false); 360 SetActionCallback(m_RightButtonAction, m_ButtonActionTriggeredDelegate, false); 361 SetActionCallback(m_MiddleButtonAction, m_ButtonActionTriggeredDelegate, false); 362 SetActionCallback(m_ForwardButtonAction, m_ButtonActionTriggeredDelegate, false); 363 SetActionCallback(m_BackButtonAction, m_ButtonActionTriggeredDelegate, false); 364 } 365 366 m_LastTime = default; 367 m_LastStickValue = default; 368 } 369 370 private void TryFindCanvas() 371 { 372 m_Canvas = m_CursorGraphic?.GetComponentInParent<Canvas>(); 373 } 374 375 private void TryEnableHardwareCursor() 376 { 377 var devices = InputSystem.devices; 378 for (var i = 0; i < devices.Count; ++i) 379 { 380 var device = devices[i]; 381 if (device.native && device is Mouse mouse) 382 { 383 m_SystemMouse = mouse; 384 break; 385 } 386 } 387 388 if (m_SystemMouse == null) 389 { 390 if (m_CursorGraphic != null) 391 m_CursorGraphic.enabled = true; 392 return; 393 } 394 395 InputSystem.DisableDevice(m_SystemMouse); 396 397 // Sync position. 398 if (m_VirtualMouse != null) 399 m_SystemMouse.WarpCursorPosition(m_VirtualMouse.position.value); 400 401 // Turn off mouse cursor image. 402 if (m_CursorGraphic != null) 403 m_CursorGraphic.enabled = false; 404 } 405 406 private void UpdateMotion() 407 { 408 if (m_VirtualMouse == null) 409 return; 410 411 // Read current stick value. 412 var stickAction = m_StickAction.action; 413 if (stickAction == null) 414 return; 415 var stickValue = stickAction.ReadValue<Vector2>(); 416 if (Mathf.Approximately(0, stickValue.x) && Mathf.Approximately(0, stickValue.y)) 417 { 418 // Motion has stopped. 419 m_LastTime = default; 420 m_LastStickValue = default; 421 } 422 else 423 { 424 var currentTime = InputState.currentTime; 425 if (Mathf.Approximately(0, m_LastStickValue.x) && Mathf.Approximately(0, m_LastStickValue.y)) 426 { 427 // Motion has started. 428 m_LastTime = currentTime; 429 } 430 431 // Compute delta. 432 var deltaTime = (float)(currentTime - m_LastTime); 433 var delta = new Vector2(m_CursorSpeed * stickValue.x * deltaTime, m_CursorSpeed * stickValue.y * deltaTime); 434 435 // Update position. 436 var currentPosition = m_VirtualMouse.position.value; 437 var newPosition = currentPosition + delta; 438 439 ////REVIEW: for the hardware cursor, clamp to something else? 440 // Clamp to canvas. 441 if (m_Canvas != null) 442 { 443 // Clamp to canvas. 444 var pixelRect = m_Canvas.pixelRect; 445 newPosition.x = Mathf.Clamp(newPosition.x, pixelRect.xMin, pixelRect.xMax); 446 newPosition.y = Mathf.Clamp(newPosition.y, pixelRect.yMin, pixelRect.yMax); 447 } 448 449 ////REVIEW: the fact we have no events on these means that actions won't have an event ID to go by; problem? 450 InputState.Change(m_VirtualMouse.position, newPosition); 451 InputState.Change(m_VirtualMouse.delta, delta); 452 453 // Update software cursor transform, if any. 454 if (m_CursorTransform != null && 455 (m_CursorMode == CursorMode.SoftwareCursor || 456 (m_CursorMode == CursorMode.HardwareCursorIfAvailable && m_SystemMouse == null))) 457 m_CursorTransform.anchoredPosition = newPosition; 458 459 m_LastStickValue = stickValue; 460 m_LastTime = currentTime; 461 462 // Update hardware cursor. 463 m_SystemMouse?.WarpCursorPosition(newPosition); 464 } 465 466 // Update scroll wheel. 467 var scrollAction = m_ScrollWheelAction.action; 468 if (scrollAction != null) 469 { 470 var scrollValue = scrollAction.ReadValue<Vector2>(); 471 scrollValue.x *= m_ScrollSpeed; 472 scrollValue.y *= m_ScrollSpeed; 473 474 InputState.Change(m_VirtualMouse.scroll, scrollValue); 475 } 476 } 477 478 [Header("Cursor")] 479 [Tooltip("Whether the component should set the cursor position of the hardware mouse cursor, if one is available. If so, " 480 + "the software cursor pointed (to by 'Cursor Graphic') will be hidden.")] 481 [SerializeField] private CursorMode m_CursorMode; 482 [Tooltip("The graphic that represents the software cursor. This is hidden if a hardware cursor (see 'Cursor Mode') is used.")] 483 [SerializeField] private Graphic m_CursorGraphic; 484 [Tooltip("The transform for the software cursor. Will only be set if a software cursor is used (see 'Cursor Mode'). Moving the cursor " 485 + "updates the anchored position of the transform.")] 486 [SerializeField] private RectTransform m_CursorTransform; 487 488 [Header("Motion")] 489 [Tooltip("Speed in pixels per second with which to move the cursor. Scaled by the input from 'Stick Action'.")] 490 [SerializeField] private float m_CursorSpeed = 400; 491 [Tooltip("Scale factor to apply to 'Scroll Wheel Action' when setting the mouse 'scrollWheel' control.")] 492 [SerializeField] private float m_ScrollSpeed = 45; 493 494 [Space(10)] 495 [Tooltip("Vector2 action that moves the cursor left/right (X) and up/down (Y) on screen.")] 496 [SerializeField] private InputActionProperty m_StickAction; 497 [Tooltip("Button action that triggers a left-click on the mouse.")] 498 [SerializeField] private InputActionProperty m_LeftButtonAction; 499 [Tooltip("Button action that triggers a middle-click on the mouse.")] 500 [SerializeField] private InputActionProperty m_MiddleButtonAction; 501 [Tooltip("Button action that triggers a right-click on the mouse.")] 502 [SerializeField] private InputActionProperty m_RightButtonAction; 503 [Tooltip("Button action that triggers a forward button (button #4) click on the mouse.")] 504 [SerializeField] private InputActionProperty m_ForwardButtonAction; 505 [Tooltip("Button action that triggers a back button (button #5) click on the mouse.")] 506 [SerializeField] private InputActionProperty m_BackButtonAction; 507 [Tooltip("Vector2 action that feeds into the mouse 'scrollWheel' action (scaled by 'Scroll Speed').")] 508 [SerializeField] private InputActionProperty m_ScrollWheelAction; 509 510 private Canvas m_Canvas; // Canvas that gives the motion range for the software cursor. 511 private Mouse m_VirtualMouse; 512 private Mouse m_SystemMouse; 513 private Action m_AfterInputUpdateDelegate; 514 private Action<InputAction.CallbackContext> m_ButtonActionTriggeredDelegate; 515 private double m_LastTime; 516 private Vector2 m_LastStickValue; 517 518 private void OnButtonActionTriggered(InputAction.CallbackContext context) 519 { 520 if (m_VirtualMouse == null) 521 return; 522 523 // The button controls are bit controls. We can't (yet?) use InputState.Change to state 524 // the change of those controls as the state update machinery of InputManager only supports 525 // byte region updates. So we just grab the full state of our virtual mouse, then update 526 // the button in there and then simply overwrite the entire state. 527 528 var action = context.action; 529 MouseButton? button = null; 530 if (action == m_LeftButtonAction.action) 531 button = MouseButton.Left; 532 else if (action == m_RightButtonAction.action) 533 button = MouseButton.Right; 534 else if (action == m_MiddleButtonAction.action) 535 button = MouseButton.Middle; 536 else if (action == m_ForwardButtonAction.action) 537 button = MouseButton.Forward; 538 else if (action == m_BackButtonAction.action) 539 button = MouseButton.Back; 540 541 if (button != null) 542 { 543 var isPressed = context.control.IsPressed(); 544 m_VirtualMouse.CopyState<MouseState>(out var mouseState); 545 mouseState.WithButton(button.Value, isPressed); 546 547 InputState.Change(m_VirtualMouse, mouseState); 548 } 549 } 550 551 private static void SetActionCallback(InputActionProperty field, Action<InputAction.CallbackContext> callback, bool install = true) 552 { 553 var action = field.action; 554 if (action == null) 555 return; 556 557 // We don't need the performed callback as our mouse buttons are binary and thus 558 // we only care about started (1) and canceled (0). 559 560 if (install) 561 { 562 action.started += callback; 563 action.canceled += callback; 564 } 565 else 566 { 567 action.started -= callback; 568 action.canceled -= callback; 569 } 570 } 571 572 private static void SetAction(ref InputActionProperty field, InputActionProperty value) 573 { 574 var oldValue = field; 575 field = value; 576 577 if (oldValue.reference == null) 578 { 579 var oldAction = oldValue.action; 580 if (oldAction != null && oldAction.enabled) 581 { 582 oldAction.Disable(); 583 if (value.reference == null) 584 value.action?.Enable(); 585 } 586 } 587 } 588 589 private void OnAfterInputUpdate() 590 { 591 UpdateMotion(); 592 } 593 594 /// <summary> 595 /// Determines how the cursor for the virtual mouse is represented. 596 /// </summary> 597 /// <seealso cref="cursorMode"/> 598 public enum CursorMode 599 { 600 /// <summary> 601 /// The cursor is represented as a UI element. See <see cref="cursorGraphic"/>. 602 /// </summary> 603 SoftwareCursor, 604 605 /// <summary> 606 /// If a native <see cref="Mouse"/> device is present, its cursor will be used and driven 607 /// by the virtual mouse using <see cref="Mouse.WarpCursorPosition"/>. The software cursor 608 /// referenced by <see cref="cursorGraphic"/> will be disabled. 609 /// 610 /// Note that if no native <see cref="Mouse"/> is present, behavior will fall back to 611 /// <see cref="SoftwareCursor"/>. 612 /// </summary> 613 HardwareCursorIfAvailable, 614 } 615 616 #if UNITY_EDITOR 617 [UnityEditor.CustomEditor(typeof(VirtualMouseInput))] 618 private class VirtualMouseInputEditor : UnityEditor.Editor 619 { 620 public void OnDisable() 621 { 622 new InputComponentEditorAnalytic(InputSystemComponent.VirtualMouseInput).Send(); 623 new VirtualMouseInputEditorAnalytic(this).Send(); 624 } 625 } 626 #endif 627 } 628} 629#endif // PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI