+4 votes

I would like to know how to create a selecting rectangle like in any RTS

I want to be able to:

  1. Click the left mouse button
  2. Drag my mouse to another location
  3. Every unit that is inside the screen-aligned rectangle given by the starting and ending points is selected

It would be nice if you propose a way for both 2D and 3D.

in Engine by (32 points)
edited by

3 Answers

0 votes


I'm trying to do the same, I have some ideas to make this, maybe can help you.

1.- Create a 2d scene with a script who extends a Node2d to draw the rectangle.
2.- Add the 2d scene to a new one (in my case 3d) to see the draw
3.- Instance the script from the 2d scene in the 3d scene to get the rectangle(Rect2) when I'm dragging, , with this I will check every(charcaters) object from my 3d scene and see if they are inside of the rectangle.

When I have some of this working I will post the code.

by (50 points)

Can you please post the code?

+2 votes

Here is some code I'm using to create a rectangle on a control node to select its texture button children

func recy(event): # Rectangle draw test
    if(event.type == InputEvent.MOUSE_BUTTON):
                from  = event.pos
                to    = event.pos
                multi_select=false      # done remove rect
                update()                # draw call without rect 
    elif(event.type == InputEvent.MOUSE_MOTION):    
            to = event.pos  # update position and
            update()        # draw

# draw and select here
func _draw():

        var tl = Vector2(min(to.x,from.x),min(to.y,from.y))
        var br = Vector2(max(to.x,from.x),max(to.y,from.y))
        var tr = Vector2(br.x,tl.y)
        var bl = Vector2(tl.x,br.y)
        var my = Rect2(tl, br-tl )

        draw_rect( my, Color(0,1,0,0.2))

        draw_line( tl, tr, Color(0,1,0) )
        draw_line( tl, bl, Color(0,1,0) )
        draw_line( br, tr, Color(0,1,0) )
        draw_line( br, bl, Color(0,1,0) )

        my.pos=tl+get_global_pos() # the center container prevents me from getting an easy rect so i have to ask the button for its global rect and terfore make my mose rect global aswell

        for i in get_node("radar").get_children():
                if(my.intersects(i.button.get_global_rect() )):
                        selec_rect_items.erase( i )
#       for i in get_node("radar").get_children():
#           if(i.button):
#               pass
        for i in selec_rect_items:
by (165 points)
+2 votes

Took me some time but here is a way to select units in 2D via Ray Casts:
First the single click then the rectangle you have asked about.
Simple raycast:

var results  = get_world_2d().get_direct_space_state().intersect_point( global_mouse_pos, 1 )

next, rectangle raycast:

func test(pos_i):   
# called by a controlnode with the current global mouse position
var para = Physics2DShapeQueryParameters.new()
var rect = RectangleShape2D.new()
var rect_size = Vector2(100,100) # 2*the size of the rectagle
rect.set_extents(rect_size )
para.set_shape( rect )
para.set_transform( Matrix32(Vector2(1,0),Vector2(0,1), pos_i+rect_size))
var result  = get_world_2d().get_direct_space_state().intersect_shape( para  )
if !result.empty():
    for i in result:
        print (i.collider.get_name())

    print("nobody under rect")
by (165 points)

Um... what about Area2D
Also, this is not a ray­cast, it is, at best a shape intersection. (or if you wish, a shapecast)

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.