That you want is to capture the mouse pointer.
Call input.set_mouse_mode
it takes as parameter a MouseMode
.
Copied from the linked documentation:
enum MouseMode:
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_VISIBLE
and 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).
Example:
func _ready():
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED);
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 MouseMode
to MOUSE_MODE_VISIBLE
. You could, for example use an input action:
func _input(event):
if event.is_action_pressed("ui_cancel"):
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE);
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 thatevent.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:
func _process(_delta):
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.