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 ^^
//using sirenix.odininspector;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.UI;
namespace JL.Player.UI
{
public class GamepadCursor : MonoBehaviour
{
[SerializeField] RectTransform _cursorParent;
[SerializeField] Image _normalCursor;
[SerializeField] Image _hoverCursor;
[SerializeField] EventSystem _eventSystem;
/*[ShowInInspector, ReadOnly]*/ bool _hovering = false;
/*[ShowInInspector, ReadOnly]*/ bool _visible = false;
/*[ShowInInspector, ReadOnly]*/ bool _isGamepad = false;
/*[ShowInInspector, ReadOnly]*/ bool _isCursorFree = false;
Vector2 _halfScreen;
/*[ShowInInspector, ReadOnly]*/ Vector2 _cursorPosition;
PlayerInput _controls;
ButtonControl _uiClickButton;
private void Start()
{
_halfScreen = new Vector2(Screen.width, Screen.height) / 2;
_cursorPosition = _halfScreen;
_controls = new PlayerInput();
_controls.Enable();
}
void Update()
{
_isGamepad = InputMaster.currentInputDevice ==
InputMaster.InputDeviceType.Gamepad;
_isCursorFree = InputMaster.IsBlocked(CharCtrlType.MouseLook);
if (!_isGamepad || !_isCursorFree)
{
SetVisible(false);
if (Mouse.current != null && !_isGamepad)
{
_cursorPosition = Mouse.current.position.ReadValue();
}
else
{
_cursorPosition = _halfScreen;
}
return;
}
_hovering = _eventSystem.IsPointerOverGameObject();
SetVisible(true);
Vector2 delta = _controls.Player.Look.ReadValue<Vector2>();
_cursorPosition += delta;
_cursorPosition.x = Mathf.Clamp(_cursorPosition.x, 0, Screen.width);
_cursorPosition.y = Mathf.Clamp(_cursorPosition.y, 0, Screen.height);
//Mouse.current.WarpCursorPosition(_cursorPosition);
InputState.Change(Mouse.current.position, _cursorPosition);
_cursorParent.position = _cursorPosition;
if (_controls.UI.Click.activeControl != null)
{
_uiClickButton =
_controls.UI.Click.activeControl as ButtonControl;
}
if (_uiClickButton != null)
{
bool isPressed = _uiClickButton.IsPressed();
Mouse.current.CopyState<MouseState>(out var mouseState);
mouseState = mouseState.WithButton(MouseButton.Left, isPressed);
InputState.Change(Mouse.current, mouseState);
}
}
void SetVisible(bool isVisible)
{
_visible = isVisible;
_cursorParent.gameObject.SetActive(isVisible);
if (!isVisible)
{
_normalCursor.gameObject.SetActive(false);
_hoverCursor.gameObject.SetActive(false);
}
else
{
_normalCursor.gameObject.SetActive(!_hovering);
_hoverCursor.gameObject.SetActive(_hovering);
}
}
}
}
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)