Cursor lock and and fake cursor for WebGL

I am building a WebGL game with mouse input. I use the mouse to control movement and aiming, and I sometimes display a canvas and use the mouse to press buttons.

I lock the cursor (Cursor.lockState = CursorLockMode.Locked) in order to prevent the user from accidentally moving it outside the window. I then create a fake cursor by moving around an image on a canvas. I use Input.GetAxis to get the changes in the mouse position each frame. I use the fake cursor position for movement and aiming, and I can still detect clicks using Input.GetMouseButtonDown().

Problem: Buttons on a Canvas don’t work when the real mouse cursor is locked. Is there a standard way to feed my fake cursor position into Unity’s code so that Canvas elements will work?

Some things I’ve looked at and tried:

  • Set StandaloneInputModule.inputOverride to a custom component derived from BaseInput. Override mousePosition to return my fake cursor position. This only works if the cursor is not locked. StandaloneInputModule stops processing mouse clicks when the cursor is locked (the checks for this are in the base class PointerInputModule).

  • Create my own subclass of PointerInputModule and override Process(). I see some reports of people using this for VR, e.g. talesfromtherift.com/vr-gaze-input/. It might work, but the code looks tricky and I haven’t found good documentation for this. It looks like I’d need to do a lot more than just report a cursor location to get it to work. PointerInputModule itself is pretty complex and checks for CursorLockMode in three different places.

  • Use the new Input System and use a virtual cursor. I’ve seen a bunch of tutorials showing how to set up a gamepad cursor, but nothing about what I’m trying to do.

raycast for UI button, send click?

1 Like

The standard way to solve this is to actually unlock the cursor while an interactive UI is shown.

You can set up a custom cursor in Player Settings and make it the same image as your current canvas, this should allow you to lock and unlock the cursor without the cursor sprite changing - but you may need to reposition it when performing the unlock.

You can also catch the window focus or mouse over events, provided they do run in a webgl app when leaving the container window. This would allow you to disable input in the game and perhaps hide the cursor while the “true” cursor is outside the app window.

Unlocking the cursor when showing a menu works perfectly, and simplifies a lot of other things. Thank you!

I don’t need to do anything with mouse over or window focus events for now, as I do not allow actual gameplay unless the cursor is locked. But good to know.

Follow-up question: For my fake cursor, I have my own mouse sensitivity setting that I use to scale the values I get from Input.GetAxis. This makes for a jarring transition when I lock and unlock the cursor and it suddenly starts moving faster or slower. And I’d prefer not to have my own sensitivity setting anyway. Is there a way to make my fake cursor sensitivity match the system settings for the real cursor? I tried Input.GetAxisRaw, still had the same problem.

1 Like

You could take over cursor drawing entirely and keep the hardware/OS Cursor hidden (but not locked). Though that means you may need that sensitivity slider.