In the post Godot Tutorial – Part 5: Player movement, we saw how to move the player using keyboard and joypad. In the comments of that tutorial, a reader asked for suggestions on how to move the player by dragging it around with the mouse. In this small bonus tutorial, we will see how to drag KinematicBody2D nodes.
Warning! This tutorial require the CollisionShape2D node added in Part 6: Physics and collisions tutorial, so don’t read this tutorial before you complete that!
Preparing player node for dragging
Let’s open the SimpleRPG project.
First thing, select the Player node. In the Inspector, in the properties inherited from CollisionObject2D, enables the Pickable option.
A pickable object can detect the mouse pointer entering/leaving, and if the mouse pointer is inside it, report input events through the _input_event() method. We will use the press and release of the left mouse button respectively to begin and end player dragging.
Open Player’s script. First, we will need a variable to store whether dragging is active or not. Write this code next to the declaration of the speed variable:
# Player dragging flag var drag_enabled = false
Now, we need to add two new functions to the script.
The first is _input_event(), which, as we mentioned earlier, is used to intercept mouse events that specifically involve the player.
func _input_event(viewport, event, shape_idx): if event is InputEventMouseButton: if event.button_index == BUTTON_LEFT: drag_enabled = event.pressed
The working of this function is simple: when an InputEventMouseButton event is detected (i.e. the press or release of a mouse button), we check that the button pressed is the left one. If so, the drag_enabled variable is set to the value of event.pressed: true if the button is pressed and false otherwise.
The second function we must add is _input(). This function (inherited from Node) is called when any input is detected. We must implement this function because it can happen that the mouse button is released when the pointer is outside the player. In that case, _input_event() does not receive the event, and the dragging procedure will not be completed correctly.
func _input(event): if event is InputEventMouseButton: if event.button_index == BUTTON_LEFT and not event.pressed: drag_enabled = false
The code is very similar to the previous function, but we only handle the release of the mouse button. Otherwise, player dragging would start by clicking anywhere on the screen.
Finally, in the _physics_process() function, insert this code immediately before calling move_and_collide():
# If dragging is enabled, use mouse position to calculate movement if drag_enabled: var new_position = get_global_mouse_position() movement = new_position - position;
If drag_enabled is true, the function calculates player’s movement as the difference between the position of the mouse in global coordinates (obtained by calling the function get_global_mouse_position()) and the current position of Player. The move_and_collide() function will perform this movement, placing the player at the cursor position.
Limiting movement speed
The code we wrote works, but it allows us to move the player much faster than when we use the keyboard or the joypad. So, we want to limit the speed to the value set in the speed variable.
To do this, we need to check if the calculated movement is greater than what is possible at maximum speed. In that case, we need to recalculate the movement so that it’s in the same direction, but limited in length.
Enter this code before the move_and_collide() function, inside the if block:
if movement.length() > (speed * delta): movement = speed * delta * movement.normalized()
Now, the player can’t move faster than its maximum speed:
Below is the updated script code:
extends KinematicBody2D # Player movement speed export var speed = 75 # Player dragging flag var drag_enabled = false func _physics_process(delta): # Get player input var direction: Vector2 direction.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left") direction.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up") # If input is digital, normalize it for diagonal movement if abs(direction.x) == 1 and abs(direction.y) == 1: direction = direction.normalized() # Calculate movement var movement = speed * direction * delta # If dragging is enabled, use mouse position to calculate movement if drag_enabled: var new_position = get_global_mouse_position() movement = new_position - position; if movement.length() > (speed * delta): movement = speed * delta * movement.normalized() # Apply movement move_and_collide(movement) func _input_event(viewport, event, shape_idx): if event is InputEventMouseButton: if event.button_index == BUTTON_LEFT: drag_enabled = event.pressed func _input(event): if event is InputEventMouseButton: if event.button_index == BUTTON_LEFT and not event.pressed: drag_enabled = false
In this tutorial, we have learned how to use mouse events to move a KinematicBody2D node by dragging it across the screen. With a few minor modifications, it is also possible to create a point and click movement system (I leave this as an exercise, but if you need help, leave a comment below).