Hello to everyone ! I I was trying to replicate RTS Style selection of units. For example, in Age of Empires you can select units by clicking on them one by one or with a selection rectangle, clicking and dragging with mouse. I learnt about selection with a rectangle in Youtube tutorials:
Kids can Code Tutorial
LegionGames Tutorial
Both tutorials are amazing but if I try to implement "one by one" selection, it doesnt work, when I click on a unit, it never deselects...
I´ve tried using signals and imputevent but in both cases, the outcome is the same.
I realize that it's the first time that I have a project with multiple objects requiring input, so any suggestios are welcome
Thank you very much in advance !
Just in case, here's my code:
Main Scene is a Node2D
extends Node2D
var dragging:bool = false
var selected_units:Array =[]
#Start of selection rectangle
var drag_start:Vector2
#RectangleShape2D is a built in type of rectangle that can detect collisions
var selection_rectangle:RectangleShape2D = RectangleShape2D.new()
# Reference to Node that will draw rectangular selection
onready var selection_draw:Node2D = $DrawSelection
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
# WITH THIS APROACH I CANT SELECT UNITS ONE BY ONE AND WITH RECTANGLE. IF I USE INPUT EVENT HERE IN MAIN, THE FUNCTION DOESN'T
# WORK, IF I USE _UNHANDLED_INPUT IN UNITS, SOMETHING GOES WRONG
func _unhandled_input(event):
if event is InputEventMouseButton and event.button_index == BUTTON_LEFT:
# When the mouse button is pressed, then the dragging starts
if event.pressed == true:
# In case there are selected units, a new selection cancels the previous one
for unit in selected_units:
#unit.collider.deselect()
unit.collider.toggle_selected(false)
selected_units = []
dragging = true
drag_start = event.position
# If I'm already dragging and release mouse button
elif dragging == true:
dragging = false
var drag_end:Vector2 = event.position
selection_draw.update_selection_rect(drag_start,event.position,dragging)
drag_end = event.position
# Godot Docs says that RectangleShape2D extents are half its size, so...
selection_rectangle.extents = (drag_end - drag_start) / 2
#Now collision check starts. Query space...
var space = get_world_2d().direct_space_state
var query = Physics2DShapeQueryParameters.new()
#Because my Units are Area2d
query.collide_with_areas = true
#Assing the RectangleShape2D
query.set_shape(selection_rectangle)
#Position
query.transform = Transform2D(0, (drag_end + drag_start)/2)
#Selected units will be those intersected by the RectangleShape2D
selected_units = space.intersect_shape(query)
#print(selected_units)
for unit in selected_units:
unit.collider.toggle_selected(true)
if dragging == true:
if event is InputEventMouseMotion:
selection_draw.update_selection_rect(drag_start,event.position,dragging)
This is the scrypt of Main's child node that draws selection rectangle:
extends Node2D
var start:Vector2
var end:Vector2
var is_dragging:bool = false
func update_selection_rect(from:Vector2,to:Vector2,drag:bool):
start = from
end = to
is_dragging = drag
update()
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
func _draw():
if is_dragging == true:
draw_rect(Rect2(start,end - start),Color.blue,false)
This is the scrypt attached to Units
extends Area2D
class_name Unit
# Units should be selected with left click (or with selection rectangle) and moved with right click
var selected:bool = false
onready var selection_icon:Sprite = $Selected
onready var tween:Tween = $Tween
func move(to:Vector2):
var destination:Vector2 = to
tween.interpolate_property(self,"position",self.position,destination,2.0,Tween.TRANS_LINEAR,Tween.EASE_IN_OUT)
func toggle_selected(value:bool):
selected = value
selection_icon.visible = value
func _input_event(_viewport, event, _shape_idx):
if event is InputEventMouseButton and event.button_index == BUTTON_LEFT and event.pressed == true:
self.toggle_selected(not selected)
print(selected)