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!
2 Answers
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
orText.box_selection_3d
in the ground mesh logic
-
$\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$– HeheheheCommented Apr 6, 2015 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$– ChebhouCommented Apr 6, 2015 at 23:08
-
$\begingroup$ I just scaled everything up and now it seems to work perfect. Thanks! $\endgroup$– HeheheheCommented Apr 6, 2015 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$– ChebhouCommented Apr 6, 2015 at 23:25
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.
-
$\begingroup$ Hey, no prob. were you following CGAstyms mouse track tutorial? that's what got me there. $\endgroup$– ruckusCommented Apr 5, 2015 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$– HeheheheCommented Apr 5, 2015 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$– ruckusCommented Apr 5, 2015 at 20:05
-
-
$\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$– ruckusCommented Apr 6, 2015 at 16:43