Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Moving the mouse cursor with the gamepad joystick?

Discussion in 'Scripting' started by Lethn, May 29, 2020.

  1. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    I've just been experimenting with the input.manager and once I got my hands on the migration documentation it was all pretty straight forward. I have some basic movement going on with my controller but I can't figure out how to get the mouse cursor to move, is this possible?

    I'm thinking of something like in Apex Legends when you bring up the inventory and can move the cursor and select items with the analogue stick on a controller.

     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,723
  3. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    Of course there's a built in function that I missed, thanks a bunch lol.
     
  4. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    Okay, I did some checking and I'm at least getting input now but because the WarpCursorPosition only seems to take new vectors I don't know how to increase it, what would be the correct syntax for adding in a certain vector 2 direction rather than creating new coordinates each time?
     
  5. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,723
    You can pass any Vector2 you want... it's just a normal method with a Vector2 argument (in screen coordinates)
    Code (CSharp):
    1. Vector2 myVector = <get a Vector2 any way you want>
    2. Mouse.current.WarpCursorPosition(myVector);
     
  6. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,723
    Are you asking how to move it with a joystick for example? Just take the current cursor position, add some diff based on the current joystick input (don't forget to factor in Time.deltaTime), and feed that into the cursor warp method.
     
  7. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    Hang on this will make more sense if I post code.

    Code (CSharp):
    1.             playerControls.XboxActionMap.XboxMoveCursorLeft.performed += ctx => IncreaseVectorLeft();
    2.             playerControls.XboxActionMap.XboxMoveCursorRight.performed += ctx => IncreaseVectorRight();
    3.             playerControls.XboxActionMap.XboxMoveCusorDown.performed += ctx => IncreaseVectorDown();
    4.             playerControls.XboxActionMap.XboxMoveCusorUp.performed += ctx => IncreaseVectorUp();
    5.  
    6.  
    7.     public void IncreaseVectorLeft()
    8.  
    9.     {
    10.         Vector2 moveVectorLeft = new Vector2(-1, 0);
    11.         Mouse.current.WarpCursorPosition(moveVectorLeft);
    12.     }
    13.  
    14.     public void IncreaseVectorRight()
    15.  
    16.     {
    17.         Vector2 moveVectorRight = new Vector2(1, 0);
    18.         Mouse.current.WarpCursorPosition(moveVectorRight);
    19.     }
    20.  
    21.     public void IncreaseVectorUp()
    22.  
    23.     {
    24.         Vector2 moveVectorUp = new Vector2(0, 1);
    25.         Mouse.current.WarpCursorPosition(moveVectorUp);
    26.     }
    27.  
    28.     public void IncreaseVectorDown()
    29.  
    30.     {
    31.         Vector2 moveVectorDown = new Vector2(0, -1);
    32.         Mouse.current.WarpCursorPosition(moveVectorDown);
    33.     }
    This is what I've done so far, the annoying thing is I have very similar code and it works great with my player but the cursor just barely moves and sticks to one position rather than increments. Is this because I don't have Time.deltaTime or something else?

    It's because I've only just started with InputManager and don't really know much about WarpCursorPosition.
     
  8. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,723
    WarpCursorPosition takes an absolute screen position. So if you want it to move around like in your Apex Legends video, you need to take the current position, add something to it, then put the result back into WarpPosition. For example:
    Code (CSharp):
    1.     public void IncreaseVectorLeft()
    2.     {
    3.         Vector2 moveVectorLeft = new Vector2(-1, 0);
    4.         Vector2 currentPosition = Mouse.current.position.ReadValue();
    5.         Vector2 newPosition = currentPosition + moveVectorLeft;
    6.         Mouse.current.WarpCursorPosition(newPosition);
    7.     }
    But your code is missing two big things to make this work nicely:
    • You're ignoring the context parameter from the input callbacks which means you can't tell how MUCH the joystick is tilted to adjust the speed of cursor movement
    • You're not factoring in Time.deltaTime so your cursor movement will be framerate-dependent.
     
    Lethn likes this.
  9. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    1,583
    I'll need to do some more reading up on this so I understand the input manager system properly when it comes to the controller, the reason everything worked fine with the other joystick was because I was using a rigidbody which makes more sense now, thanks for the help.
     
  10. harryzhu45

    harryzhu45

    Joined:
    Jul 29, 2019
    Posts:
    1
    Hi, i'm using the above code right now but the mouse isn't working. Can I ask what is the action type and control type for the above Input? Also what's exactly missing from the code above?
     
  11. Damiano96

    Damiano96

    Joined:
    Sep 16, 2019
    Posts:
    4
    Hi guys! I know its a bit late but just a quick question! I cant access the "Mouse" class. I assume I'm missing the correct name space? Would you be able to help at all?
    Thankyou very much!
     
  12. Conabe

    Conabe

    Joined:
    Oct 8, 2020
    Posts:
    3
    UnityEngine.InputSystem
     
  13. xtdiwd

    xtdiwd

    Joined:
    Jul 25, 2020
    Posts:
    110
    I have the error
    InputSystem don't exist in the UnityEngine namaspace
     
  14. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    626
    Ok, I didn't get the hover graphic to show up when hovering UI in this example (still working on it) but the gamepad mouse cursor works perfectly fine, UI Buttons will react when hovered and can be clicked.
    Just make sure to setup a controller button in the UI.Click Action in your input Asset, as well as a "Look" Action which has a binding to the gamepad-stick.

    Look is used to move the mouse.
    Click is used to click on buttons.

    I made this based on the "Gamepad Mouse Cursor" example which comes as extra package with the new input system.
    While the example works, mostly, I found it way to confusing, so I stripped out what I needed.
    Mostly because I didn't manage to allow fluid switches from Gamepad, to Mouse and vice versa. Actually, when using it (enable/disable) the mouse always stopped working

    The "Input Master" (you will have to comment that out) is some self written thing to see which device is currently used and if the player has control over the camera. Just replace those lines with your own ingame logic.

    Also you have to use your own input asset in the (UI) Event System. (ps: you cannot configure the default input asset, it will always reset the state)

    _cursorParent is just an empty that holds the two cursor graphics

    Have fun ^^

    Code (CSharp):
    1.  
    2. //using sirenix.odininspector;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5. using UnityEngine.InputSystem;
    6. using UnityEngine.InputSystem.Controls;
    7. using UnityEngine.InputSystem.LowLevel;
    8. using UnityEngine.UI;
    9. namespace JL.Player.UI
    10. {
    11.     public class GamepadCursor : MonoBehaviour
    12.     {
    13.         [SerializeField] RectTransform _cursorParent;
    14.         [SerializeField] Image _normalCursor;
    15.         [SerializeField] Image _hoverCursor;
    16.         [SerializeField] EventSystem _eventSystem;
    17.         /*[ShowInInspector, ReadOnly]*/ bool _hovering = false;
    18.         /*[ShowInInspector, ReadOnly]*/ bool _visible = false;
    19.         /*[ShowInInspector, ReadOnly]*/ bool _isGamepad = false;
    20.         /*[ShowInInspector, ReadOnly]*/ bool _isCursorFree = false;
    21.         Vector2 _halfScreen;
    22.         /*[ShowInInspector, ReadOnly]*/ Vector2 _cursorPosition;
    23.         PlayerInput _controls;
    24.         ButtonControl _uiClickButton;
    25.         private void Start()
    26.         {
    27.             _halfScreen = new Vector2(Screen.width, Screen.height) / 2;
    28.             _cursorPosition = _halfScreen;
    29.             _controls = new PlayerInput();
    30.             _controls.Enable();
    31.         }
    32.         void Update()
    33.         {
    34.             _isGamepad = InputMaster.currentInputDevice ==
    35.                 InputMaster.InputDeviceType.Gamepad;
    36.             _isCursorFree = InputMaster.IsBlocked(CharCtrlType.MouseLook);
    37.             if (!_isGamepad || !_isCursorFree)
    38.             {
    39.                 SetVisible(false);
    40.                 if (Mouse.current != null && !_isGamepad)
    41.                 {
    42.                     _cursorPosition = Mouse.current.position.ReadValue();
    43.                 }
    44.                 else
    45.                 {
    46.                     _cursorPosition = _halfScreen;
    47.                 }
    48.                 return;
    49.             }
    50.             _hovering = _eventSystem.IsPointerOverGameObject();
    51.             SetVisible(true);
    52.             Vector2 delta = _controls.Player.Look.ReadValue<Vector2>();
    53.             _cursorPosition += delta;
    54.             _cursorPosition.x = Mathf.Clamp(_cursorPosition.x, 0, Screen.width);
    55.             _cursorPosition.y = Mathf.Clamp(_cursorPosition.y, 0, Screen.height);
    56.             //Mouse.current.WarpCursorPosition(_cursorPosition);
    57.             InputState.Change(Mouse.current.position, _cursorPosition);
    58.             _cursorParent.position = _cursorPosition;
    59.             if (_controls.UI.Click.activeControl != null)
    60.             {
    61.                 _uiClickButton =
    62.                     _controls.UI.Click.activeControl as ButtonControl;
    63.             }
    64.             if (_uiClickButton != null)
    65.             {
    66.                 bool isPressed = _uiClickButton.IsPressed();
    67.                 Mouse.current.CopyState<MouseState>(out var mouseState);
    68.                 mouseState = mouseState.WithButton(MouseButton.Left, isPressed);
    69.                 InputState.Change(Mouse.current, mouseState);
    70.             }
    71.         }
    72.         void SetVisible(bool isVisible)
    73.         {
    74.             _visible = isVisible;
    75.             _cursorParent.gameObject.SetActive(isVisible);
    76.             if (!isVisible)
    77.             {
    78.                 _normalCursor.gameObject.SetActive(false);
    79.                 _hoverCursor.gameObject.SetActive(false);
    80.             }
    81.             else
    82.             {
    83.                 _normalCursor.gameObject.SetActive(!_hovering);
    84.                 _hoverCursor.gameObject.SetActive(_hovering);
    85.             }
    86.         }
    87.     }
    88. }
    89.  

    PS: make sure you don't block the mouse cursor with the cursor graphics - so remove the graphic raycaster from the canvas that holds the gamepad cursor (you probably want it to be it's own canvas so it will always be on top - otherwise, use a canvasGroup on the cursor and disable "blocksRaycast" there)
     
    Last edited: May 16, 2021
  15. xtdiwd

    xtdiwd

    Joined:
    Jul 25, 2020
    Posts:
    110
  16. Pnvanol

    Pnvanol

    Joined:
    Jan 11, 2016
    Posts:
    110
    How are we supposed to switch between gamepad cursor and normal game pad controls (running and firing for example)? once I put the cursor in the scene, the action map is always gamepadcursor...

    Edit: I am using the example provided in the new unity system package in package manager
     
    Last edited: Jun 29, 2021
  17. a_ercanbrack

    a_ercanbrack

    Joined:
    Dec 15, 2020
    Posts:
    2
    Pnvanol, which package are you using? I'm trying to fix this issue with my inventory system. I am also using the new Input actions system and I have two action maps, one for all game interactions and the other for menus. I'd like to see a successful implementation of this.

    Thank you
     
  18. wpetillo

    wpetillo

    Joined:
    May 23, 2017
    Posts:
    24
    For people (like me) who find this thread on an internet search and want some code that actually moves the cursor with a joystick as if it were the mouse. WarpCursorPosition is a bit of a nuisance in getting smooth, balanced movement in all directions because it applies FloorToInt on its inputs.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.InputSystem;
    3.  
    4. public class GamepadCursor : MonoBehaviour
    5. {
    6.     [Tooltip("Higher numbers for more mouse movement on joystick press." +
    7.              "Warning: diagonal movement lost at lower sensitivity (<1000)")]
    8.     public Vector2 sensitivity = new Vector2(1500f, 1500f);
    9.     [Tooltip("Counteract tendency for cursor to move more easily in some directions")]
    10.     public Vector2 bias = new Vector2(0f, -1f);
    11.    
    12.     // Cached variables
    13.     Vector2 rightStick;
    14.     Vector2 mousePosition;
    15.     Vector2 warpPosition;
    16.    
    17.     // Stored for next frame
    18.     Vector2 overflow;
    19.    
    20.     void Update()
    21.     {
    22.         // Get the joystick position
    23.         rightStick = Gamepad.current.rightStick.ReadValue();
    24.        
    25.         // Prevent annoying jitter when not using joystick
    26.         if (rightStick.magnitude < 0.1f) return;
    27.        
    28.         // Get the current mouse position to add to the joystick movement
    29.         mousePosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
    30.        
    31.         // Precise value for desired cursor position, which unfortunately cannot be used directly
    32.         warpPosition = mousePosition + bias + overflow + sensitivity * Time.deltaTime * rightStick;
    33.        
    34.         // Keep the cursor in the game screen (behavior gets weird out of bounds)
    35.         warpPosition = new Vector2(Mathf.Clamp(warpPosition.x, 0, Screen.width), Mathf.Clamp(warpPosition.y, 0, Screen.height));
    36.        
    37.         // Store floating point values so they are not lost in WarpCursorPosition (which applies FloorToInt)
    38.         overflow = new Vector2(warpPosition.x % 1, warpPosition.y % 1);
    39.        
    40.         // Move the cursor
    41.         Mouse.current.WarpCursorPosition(warpPosition);
    42.     }
    43. }
     
    Damjan-Mozetic likes this.