The Godot Q&A is currently undergoing maintenance!

Your ability to ask and answer questions is temporarily disabled. You can browse existing threads in read-only mode.

We are working on bringing this community platform back to its full functionality, stay tuned for updates.

godotengine.org | Twitter

+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

PS:
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

Hi,

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):
        if(event.button_index==1):
            if(event.is_pressed()):
                from  = event.pos
                to    = event.pos
                multi_select=true       
            else:
                multi_select=false      # done remove rect
                update()                # draw call without rect 
    elif(event.type == InputEvent.MOUSE_MOTION):    
        if(multi_select):                           
            to = event.pos  # update position and
            update()        # draw






# draw and select here
func _draw():

    if(multi_select):
        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(i.button):
                i.image.set_modulate(global.cl)
                if(my.intersects(i.button.get_global_rect() )):
                    i.image.set_modulate(global.ch)
                    if(-1==selec_rect_items.find(i)):
                        selec_rect_items.append(i)
                else:
                    if(-1!=selec_rect_items.find(i)):
                        selec_rect_items.erase( i )
    else:
#       for i in get_node("radar").get_children():
#           if(i.button):
#               pass
        for i in selec_rect_items:
            i.image.set_modulate(global.cl)
            i.set_selected(true)
        selec_rect_items.clear()
by (177 points)
+2 votes

Took me some time but here is a way to select units in 2D via Ray Casts:
http://docs.godotengine.org/en/latest/tutorials/ray-casting.html
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())
        i.collider.get_node("Sprite").set_modulate(Color(randf(),randf(),randf()))

else:
    print("nobody under rect")
by (177 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 Frequently asked questions and 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 [email protected] with your username.