Godot simplified drag n drop tutorial
I was "following" Generalist Programmer's Godot drag and drop tutorial and I made a few refinements that I wanted to share. Please read the tutorial and then follow along.
Drop-in behaviour
Wouldn't it be cool to give a node drag and drop behaviour simply by dropping in a node with the script, independent of any other scripting?
We can do this by creating a new child node (here called Drag-and-drop Dropin), and attaching the script to it. It must extend from the dropin Node2D.
In the script itself we make two changes:
- Disconnect the KinematicBody2D's input_event signal, and write it in code in the dropin's _ready function. Call it on it's parent:
get_parent().connect("input_event", self, "_on_KinematicBody2D_input_event")
. We want to process theinput_event
of the KinematicBody2D, not the dropin. - In the
_process
function, assign the mouse position to the parent of the dropin:get_parent().position = Vector2(mousepos.x, mousepos.y)
. - Same for the last line in the script where we set the position of the parent.
The script then reads as follows:
extends Node2D
var dragging = false
signal dragsignal
func _ready():
connect("dragsignal", self, "_set_drag_pc")
get_parent().connect("input_event", self, "_on_KinematicBody2D_input_event")
func _process(delta):
if dragging:
var mousepos = get_viewport().get_mouse_position()
get_parent().position = Vector2(mousepos.x, mousepos.y)
func _set_drag_pc():
dragging = !dragging
func _on_KinematicBody2D_input_event(viewport, event, shape_idx):
if event is InputEventMouseButton:
if event.button_index == BUTTON_LEFT and event.pressed:
emit_signal("dragsignal")
elif event.button_index == BUTTON_LEFT and !event.pressed:
emit_signal("dragsignal")
elif event is InputEventScreenTouch:
if event.pressed and event.get_index() == 0:
get_parent().position = event.get_position()
Simplifying the script
I was refactoring this, and it turns out we can simplify this further.
- We don't need a dragsignal because the signal is both emitted and consumed within the same script. We can just replace the
emit_signal
calls with calls to_set_drag_pc()
. We can then remove theconnect()
call in the_ready
function, and thesignal dragsignal
. - The same function is called when the left mouse button is both pressed and not pressed, so we can remove that conditional and remove the
elif
statement in the input event handler. - As there is only one invocation of
_set_drag_pc()
we can inline it.
The final script becomes:
extends Node2D
var dragging = false
func _ready():
get_parent().connect("input_event", self, "_on_KinematicBody2D_input_event")
func _process(delta):
if dragging:
var mousepos = get_viewport().get_mouse_position()
get_parent().position = Vector2(mousepos.x, mousepos.y)
func _on_KinematicBody2D_input_event(viewport, event, shape_idx):
if event is InputEventMouseButton:
if event.button_index == BUTTON_LEFT:
dragging = !dragging
elif event is InputEventScreenTouch:
if event.pressed and event.get_index() == 0:
get_parent().position = event.get_position()
So this dropin can now be added to any node to add Drag-and-Drop behaviour. I think the script can be improved a little so that the node is not centered under the mouse cursor but takes account the offset where it is picked up.
Let me know your thoughts! #godot