That you want is to capture the mouse pointer.
input.set_mouse_mode it takes as parameter a
Copied from the linked documentation:
MOUSE_MODE_VISIBLE = 0 --- Makes the mouse cursor visible if it is hidden.
MOUSE_MODE_HIDDEN = 1 --- Makes the mouse cursor hidden if it is visible.
MOUSE_MODE_CAPTURED = 2 --- Captures the mouse. The mouse will be hidden and its position locked at the center of the screen.
Note: If you want to process the mouse's movement in this mode, you need to use InputEventMouseMotion.relative.
MOUSE_MODE_CONFINED = 3 --- Makes the mouse cursor visible but confines it to the game window.
MOUSE_MODE_HIDDEN let the mouse pointer, visible or hidden.
MOUSE_MODE_CONFINED limits the movement of the mouse pointer to the window, that means it gets stuck at the edge, and we do not want that. However, with
MOUSE_MODE_CAPTURED it will register relative movement, but the mouse pointer remains at the center (which also means that anything that relies on the position of the pointer won't work).
With the mouse pointer captured, your camera script should work.
And you encounter a couple problems:
- You can't use the mouse to close the game window.
- Any UI won't work.
That means you are going to need a way to change the
MOUSE_MODE_VISIBLE. You could, for example use an input action:
You can imagine that would also include code that open the in-game menu with the option to close the game among other things (e.g. set the mouse sensitivity). Or if the game is windowed, you can just close the window. Then, when the menu is closed, you would then set
MOUSE_MODE_CAPTURED again for the camera to continue to work.
You probably want to pause the game while the menu is open. You can use
get_tree().paused = true; for that. *Set to
false to un-pause, of course. Remember to set the pause_mode of your nodes appropriately. You may also be interested in using
Engine.time_scale = 0.0; (which would also stop animated shaders).
On the flip side, if you don't have a menu to show at all, you probably want to recapture the mouse when the user clicks on the window. In that case, handle the click input as you normally would, and check
Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE, if that is the case, capture the mouse pointer. You may want to call
get_tree().set_input_as_handled() if you want to avoid that click event being handled by some other script afterwards.
I suppose there is another way to go about this, and that is to move the mouse pointer.
To do that, you can call
get_viewport().warp_mouse(Vector2 to_position) or
Input.warp_mouse_position(Vector2 to). In my tests these do the same thing.
These would also trigger an input event. They are different in that
event.relative will be
(0, 0) for these events. Which is not much trouble for you.
With that, I present to you, poor man's mouse capture:
var viewport = get_viewport();
viewport.warp_mouse(viewport.size * 0.5);
You probably want to set a variable to decide if you want to capture or not, and set an input action to change it… Yeah, the stuff I explained above. I'd recommend do it with
set_mouse_mode, however, many recommend to write your own code to keep the mouse pointer centered (a solution for game development frameworks that do not have this built in) that probably people would suggest it if I don't mention it. It may also lead people searching for "how to return the mouse to the center of the screen in Godot?" to
set_mouse_mode, which is what they probably need.