3
$\begingroup$

I am making an rts in blender and do not no too much about Python. Right now I have a cube (which will later be a character with an animation) that follows a mouse tracker (a cone facing down that moves wherever you click and the cube tracks and follows it. If I duplicated them then all the cubes would go wherever I would click to place the mousetracker. How could I make it so you can "select" a cube with a rectangular selection box (or clicking it with a mouse button that is different to the mouse button used to move the mousetracker) please do not direct me to any tutorials as I would like to use the mousetracker to move them. Help would be appreciated! Thanks!

$\endgroup$
  • $\begingroup$ do you want to know how to select different ones, or draw a box? $\endgroup$ – Scalia Apr 5 '15 at 2:03
  • $\begingroup$ cause you kinda didn't say which you'd prefer. $\endgroup$ – Scalia Apr 5 '15 at 2:17
1
$\begingroup$

these are two methods for box selection :


Based on the screen coordinates :

  • save first and last mouse positions in the screen while holding LMB
  • any object that has a property Select and is inside the box is selected
  • Draw a box using stored mouse position

Note the selection is working great but the box drawing has some distortion around the edges due to the camera to world space transform ( I didn't get it right )

def box_selection():

        scene = logic.getCurrentScene()
        cont = logic.getCurrentController()
        own = cont.owner

        mouse_Lclick = cont.sensors["Mouse_leftclick"] 

        if mouse_Lclick.positive :
            if not own['hold'] :
               own['m_x_old'] = mouse_Lclick.position[0]
               own['m_y_old'] = mouse_Lclick.position[1]
               own['m_x_new'] = mouse_Lclick.position[0] # to avoid drawing a bow at first
               own['m_y_new'] = mouse_Lclick.position[1] # from previous data
               own['hold'] = True
            else :
               own['m_x_new'] = mouse_Lclick.position[0]
               own['m_y_new'] = mouse_Lclick.position[1]

            draw_box(own['m_x_old'],own['m_y_old'],own['m_x_new'],own['m_y_new'])        

        else :
            if own['hold'] :
                select_inside(own['m_x_old'],own['m_y_old'],own['m_x_new'],own['m_y_new'])
            own['hold'] = False


def  select_inside(p1_x,p1_y, p2_x,p2_y):

        if p1_x > p2_x :
            p = p2_x
            p2_x = p1_x
            p1_x = p

        if p1_y > p2_y :
            p = p2_y
            p2_y = p1_y
            p1_y = p

        scene = logic.getCurrentScene()
        cam = scene.active_camera

        for obj in scene.objects :
            if 'Select' in obj.getPropertyNames() :
                print('select found!', obj.name)
                pos = cam.getScreenPosition(obj)
                x = pos[0] * render.getWindowWidth()
                y = pos[1] * render.getWindowHeight()
                if x > p1_x and y > p1_y and x < p2_x and y < p2_y :
                    print('obj inside')
                    obj['Select'] = True


def draw_box(x1,y1, x2,y2):  # there is some distortion in the drawing

        p1 = ( min(x1, x2), min(y1, y2))
        p2 = ( max(x1, x2), min(y1, y2))
        p3 = ( max(x1, x2), max(y1, y2))
        p4 = ( min(x1, x2), max(y1, y2))

        scene = logic.getCurrentScene()
        cam = scene.active_camera

        print(p1, p3)

        rect =[p1, p2, p3, p4]
        w = render.getWindowWidth()
        h = render.getWindowHeight()
        vects =[]
        for p in rect :
            x = 1 - (float(p[0]) /float(w))
            y = 1 - (float(p[1]) /float(h))
            v1 = cam.getScreenVect( x, y)
            v1[2] = -0.95
            v = cam.camera_to_world*v1 #cam.getScreenVect( x, y) 
            vects.append(v)

        render.drawLine(vects[0], vects[1], (1,0,1))
        render.drawLine(vects[1], vects[2], (1,0,1))
        render.drawLine(vects[2], vects[3], (1,0,1))
        render.drawLine(vects[3], vects[0], (1,0,1))

Based on the top view and the ground (3d space ):

  • get first and last mouse hit position on the ground mesh
  • objects that have property Select and are inside the box are selected
  • draw a box using the stored mouse position

def box_selection_3d():

        scene = logic.getCurrentScene()
        cont = logic.getCurrentController()
        own = cont.owner

        mouse_over = cont.sensors["Mouse_Over"] 
        mouse_Lclick = cont.sensors["Mouse_leftclick"] 

        if mouse_Lclick.positive :
            if not own['hold'] :
               own['m_x_old'] = mouse_over.hitPosition[0]
               own['m_y_old'] = mouse_over.hitPosition[1]
               own['m_x_new'] = mouse_over.hitPosition[0] # to avoid drawing a bow at first
               own['m_y_new'] = mouse_over.hitPosition[1] # from previous data
               own['hold'] = True
            else :
               own['m_x_new'] = mouse_over.hitPosition[0]
               own['m_y_new'] = mouse_over.hitPosition[1]

            draw_box_3d(own['m_x_old'],own['m_y_old'],own['m_x_new'],own['m_y_new'])        
        else :
            if own['hold'] :
                select_inside_3d(own['m_x_old'],own['m_y_old'],own['m_x_new'],own['m_y_new'])
            own['hold'] = False         

def  select_inside_3d(p1_x,p1_y, p2_x,p2_y):

        if p1_x > p2_x :
            p = p2_x
            p2_x = p1_x
            p1_x = p

        if p1_y > p2_y :
            p = p2_y
            p2_y = p1_y
            p1_y = p

        scene = logic.getCurrentScene()

        for obj in scene.objects :
            if 'Select' in obj.getPropertyNames() :
                print('select found!', obj.name)
                pos = obj.position
                x = pos[0] 
                y = pos[1] 
                if x > p1_x and y > p1_y and x < p2_x and y < p2_y :
                    print(obj.name, 'obj inside')
                    obj['Select'] = True


def draw_box_3d(x1,y1, x2,y2): #there seems to be some limitations with the hit position

        p1 = ( min(x1, x2), min(y1, y2))
        p2 = ( max(x1, x2), min(y1, y2))
        p3 = ( max(x1, x2), max(y1, y2))
        p4 = ( min(x1, x2), max(y1, y2))

        scene = logic.getCurrentScene()
        cont = logic.getCurrentController()
        own  = cont.owner

        h = own.position[2]+ 0.1
        rect =[p1, p2, p3, p4]

        vects =[]
        for p in rect :
            v = ( p[0], p[1], h)
            print(v)
            vects.append(v)

        render.drawLine(vects[0], vects[1], (1,1,0))
        render.drawLine(vects[1], vects[2], (1,1,0))
        render.drawLine(vects[2], vects[3], (1,1,0))
        render.drawLine(vects[3], vects[0], (1,1,0))`

Example simple function to clear selection or deselect before selecting other objects

def deselect_all():
        scene = logic.getCurrentScene()
        cont = logic.getCurrentController()

        mouse_Lclick = cont.sensors["Mouse_leftclick"] 

        if mouse_Rclick.positive :  #mouse_Lclick.positive or    for testing box select
            for obj in scene.objects :     
                if obj.get('Select', False) :
                    obj['Select'] = False
                    print(obj.name)

.blend file ( created by @VinceScalia )

  • hold and drag LMB to select
  • click RMB on ground to clear selection
  • select one of the methods by enabling one of python module bricks Text.box_selection or Text.box_selection_3d in the ground mesh logic
| improve this answer | |
$\endgroup$
  • $\begingroup$ Thank you! Works but I am only having one problem. The box is made right and everything but on mine when I drag to make the box ( I am using the 3d method) it is kind of shaky. On the example .blend it worked kind of like a grid when the you drag the box to make it bigger or smaller, on mine it is too shaky like you have to move the mouse a long way to make the box adjust to the size. Sorry if this is confusing, so basically my question is how could I make it so the box refreshes faster when you adjust the size. Thanks! $\endgroup$ – Hehehehe Apr 6 '15 at 23:01
  • $\begingroup$ @Hehehehe it's not a matter of refreshing speed , the problem is with mouse hit position is not so accurate ( or so I think) , you are probably working on small area $\endgroup$ – Chebhou Apr 6 '15 at 23:08
  • $\begingroup$ I just scaled everything up and now it seems to work perfect. Thanks! $\endgroup$ – Hehehehe Apr 6 '15 at 23:24
  • $\begingroup$ @Hehehehe glad it works , I'm still learning through this , that's why I can't get a good explanation for this : ) $\endgroup$ – Chebhou Apr 6 '15 at 23:25
2
$\begingroup$

Here's how to select people in general, by clicking on them with a button other then the one that sets the mouse tracker. Unfortunately, I don't know how to do a draw-box, but if someone else does, I would be super thrilled.

You need to add a mouse sensor, and set the field from the default Left-Mouse-Button to something else, like Middle-click, or Right-Mouse-Button. You also need a property on the cube; call it "active". This property needs to change to "True" when the cube is selected, and "False" when the cube is inactive; therefore, the Boolean variable type would be the best for this property, since there are only two different modes. In order to change it, you need a "Property" actuator set to toggle (that way you can deselect also). In the field with the little bullet, (the property field) enter "Active" which is the name of the property you want changed. Connect the actuator to your Mouse sensor you created earlier.

Now when you click on a cube, it will set the property "Active" to true. All you need to do is create another property sensor, set the evaluation type to "Equal" and enter true for the search, with Active as the property to be evaluated. connect this to the AND controller that allows your cube to steer toward the tracker and Viola!

Here's a blend to show you. hit Num5 to add people.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Hey, no prob. were you following CGAstyms mouse track tutorial? that's what got me there. $\endgroup$ – Scalia Apr 5 '15 at 3:27
  • $\begingroup$ yep and can I ask you one thing: is it supposed to lag so much when there is more than 6 cubes? I know it's not my computer because it has 12 gb ram and everything but just wondering maybe I did something wrong thanks $\endgroup$ – Hehehehe Apr 5 '15 at 4:24
  • $\begingroup$ If you turn on profiling and framerate it will tell you exactly which part of your computer is stressing out. It can be enabled in the "Game" menu. my guess is rasterizer is up. :{ $\endgroup$ – Scalia Apr 5 '15 at 20:05
  • $\begingroup$ Do you know how I could do that? $\endgroup$ – Hehehehe Apr 6 '15 at 2:41
  • $\begingroup$ yeah, at the top of the screen is a bunch of menus titled File; Game; Window; Help, respectively. Turn on "Profiling and framerate" under the "Game" window. $\endgroup$ – Scalia Apr 6 '15 at 16:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.