Activate object ONLY by clicking on it, NOT by dragging the mouse cursor over it.

OK, I have here 4 cubes that light up when you either mouse click on or press
the numbers 1 to 4 (on the keyboard, not the number pad). I have it to where
when you activate one cube, the others will not be when pressing another key
or clicking on another cube simultaneously. In other words, the other cubes
will be locked out when one is activated. Otherwise, things will get messed up.

Now when I click the mouse while the cursor is off the cubes and then drag it
over a cube, it will activate the active block in the Python script and light
up the cube. That too would mess things up.

Here’s where I need help. I need it to where any of the cubes do not activate by
clicking the mouse off a cube and then dragging the cursor over the cube while it’s
pressed down. I have tried nested and compound boolean if statements to no avail.

I’m fairly new to Python so the code will look messy and stuff. But at least it
it works that way. Hopefully the comments I put there will help you understand it
better. I’ll worry about code simplicity and efficiency later.


# MultiObject42.py

import bge
from bge import render, logic, events
render.showMouse (1)

cont = bge.logic.getCurrentController()
scene = bge.logic.getCurrentScene()
own = cont.owner
keyboard = bge.logic.keyboard
mouse = bge.logic.mouse

mouseOverOne = cont.sensors ["mouseOverOne"]
mouseOverTwo = cont.sensors ["mouseOverTwo"]
mouseOverThree = cont.sensors ["mouseOverThree"]
mouseOverFour = cont.sensors ["mouseOverFour"]

one = scene.objects ["one"]
two = scene.objects ["two"]
three = scene.objects ["three"]
four = scene.objects ["four"]

one.color = [0, .7, 0, True]
two.color = [0, .7, 0, True]
three.color = [0, .7, 0, True]
four.color = [0, .7, 0, True]

own["tapLock"] = False   # to prevent re-tap

leftClick = bge.logic.KX_INPUT_JUST_ACTIVATED == mouse.events [bge.events.LEFTMOUSE]
leftHold = bge.logic.KX_INPUT_ACTIVE == mouse.events [bge.events.LEFTMOUSE]
oneKeyHold = bge.logic.KX_INPUT_ACTIVE == keyboard.events [bge.events.ONEKEY]
oneKeyTap = bge.logic.KX_INPUT_JUST_ACTIVATED == keyboard.events [bge.events.ONEKEY]
twoKeyHold = bge.logic.KX_INPUT_ACTIVE == keyboard.events [bge.events.TWOKEY]
twoKeyTap = bge.logic.KX_INPUT_JUST_ACTIVATED == keyboard.events [bge.events.TWOKEY]
threeKeyHold = bge.logic.KX_INPUT_ACTIVE == keyboard.events [bge.events.THREEKEY]
threeKeyTap = bge.logic.KX_INPUT_JUST_ACTIVATED == keyboard.events [bge.events.THREEKEY]
fourKeyHold = bge.logic.KX_INPUT_ACTIVE == keyboard.events [bge.events.FOURKEY]
fourKeyTap = bge.logic.KX_INPUT_JUST_ACTIVATED == keyboard.events [bge.events.FOURKEY]

# prevents re-tap when click-holding a cube and pressing a corresponding key or vice-versa
if oneKeyHold or leftHold and mouseOverOne.positive:
    own["tapLock"] = True
if twoKeyHold or leftHold and mouseOverTwo.positive:
    own["tapLock"] = True
if threeKeyHold or leftHold and mouseOverThree.positive:
    own["tapLock"] = True
if fourKeyHold or leftHold and mouseOverFour.positive:
    own["tapLock"] = True


if own["lockOne"] == False: # other cubes are locked out when key pressing or click any other cube simultaneously
    # this block does tap and will do more than what is listed here
    if oneKeyTap and own["tapLock"] == False or leftClick and mouseOverOne.positive and own["tapLock"] == False:
        one.color = [0, 1, 0, True]
        print("one tapped")
        own["lockTwo"] = True # locks out cube 2
        own["lockThree"] = True # locks out cube 3
        own["lockFour"] = True # locks out cube 4
    # this block keeps the cube lit while the mouse or key button is down
    if oneKeyHold or leftHold and mouseOverOne.positive:
        one.color = [0, 1, 0, True]
        print("one active    #")
        own["lockTwo"] = True
        own["lockThree"] = True
        own["lockFour"] = True
    else: # resets the lock out booleans
        own["lockTwo"] = False
        own["lockThree"] = False
        own["lockFour"] = False


# the following does the same with cubes 2 to 4
if own["lockTwo"] == False:
    if twoKeyTap and own["tapLock"] == False or leftClick and mouseOverTwo.positive and own["tapLock"] == False:
        two.color = [0, 1, 0, True]
        print("two tapped")
        own["lockOne"] = True
        own["lockThree"] = True
        own["lockFour"] = True
    if twoKeyHold or leftHold and mouseOverTwo.positive:
        two.color = [0, 1, 0, True]
        print("two active    ##")
        own["lockOne"] = True
        own["lockThree"] = True
        own["lockFour"] = True
    else:
        own["lockOne"] = False
        own["lockThree"] = False
        own["lockFour"] = False

if own["lockThree"] == False:
    if threeKeyTap and own["tapLock"] == False or leftClick and mouseOverThree.positive and own["tapLock"] == False:
        three.color = [0, 1, 0, True]
        print("three tapped")
        own["lockOne"] = True
        own["lockTwo"] = True
        own["lockFour"] = True
    if threeKeyHold or leftHold and mouseOverThree.positive:
        three.color = [0, 1, 0, True]
        print("three active    ###")
        own["lockOne"] = True
        own["lockTwo"] = True
        own["lockFour"] = True
    else:
        own["lockOne"] = False
        own["lockTwo"] = False
        own["lockFour"] = False

if own["lockFour"] == False:
    if fourKeyTap and own["tapLock"] == False or leftClick and mouseOverFour.positive and own["tapLock"] == False:
        four.color = [0, 1, 0, True]
        print("four tapped")
        own["lockOne"] = True
        own["lockTwo"] = True
        own["lockThree"] = True
    if fourKeyHold or leftHold and mouseOverFour.positive:
        four.color = [0, 1, 0, True]
        print("four active    ####")
        own["lockOne"] = True
        own["lockTwo"] = True
        own["lockThree"] = True
    else:
        own["lockOne"] = False
        own["lockTwo"] = False
        own["lockThree"] = False

This “game” is only a sample.

Attachments

MultiObjectTest42.blend (541 KB)

Who cares if the code is shit right? – BGE Comunity

Well clearly it doesn’t work that way, or you wouldn’t be here asking for a fix. Learn to code properly and you could have done it yourself.

MultiObjectTestLOL.blend (436 KB)

To add another button you can just duplicate one and it will work just fine. This is one of the reasons why good code is necessary, because now it’s much more easy to add new buttons.

mine is more centralized , only the camera decide who is selected/active and who not


import bge


RGBA_SELECTED =     [0.0, 1.0, 0, 1.0]
RGBA_UNSELECTED =   [0.0, 0.7, 0, 1.0]






def update(cont):
    own = cont.owner
    
    if not "selected" in own:
        own["selected"] = None
        return
    
    moa = own.sensors[0]
    click = bge.logic.mouse.events[bge.events.LEFTMOUSE] == 1
    selected = None
    hit = moa.hitObject
    if hit and "selectable" in hit:
        selected = hit    




    if click:
        old_selected = own["selected"]
        if old_selected and not old_selected.invalid:
            old_selected.color = RGBA_UNSELECTED
        if selected:
            selected.color = RGBA_SELECTED
            own["selected"] = selected
    

Using a property instead of a logic brick doesn’t make it more centralized (unless you create the property on a function and call that function from the camera) and makes it more prone to name collisions. However using the rayCast (done on the MouseFocusSensor) is certainly a good idea.

Here is my version.

It implements a full selection architecture (with a single selected object).

This file demonstrates various selection options

Selection
A) select by mouse see “Selector.mouse”
B) select by keyboard key
1) individual selection logic see “one”
2) remote selection via configuration see
“Selector.keyboard.mapByProperty”, “two”, “three”, “four”

#Selection representation
Marking a selected object is implemented at the individual object. In most cases this is the best option as the object should know how to show it is selected.

Alternative is a remote selection representation. This is a sufficient option when the operation is always the same with minor object related differences. For example, you add a cursor around or on top of the selected object.

#Implemention
The code is designed to work with many configuration options. So it does not matter how many sensors you have or how they are called.

There are still some required setups:

  • selected objects get the property “selected” which will be set to True/False
  • mappedByProperty requires a property “propertyName” with the name of the property at the selectable objects. Here it is “selectionKey” which is set up at the ndividual cubes. The value of “selectionKey” is the default character representation of the key. ATTENTION: this is not keyboard mapped and does not work all over the world! Therefore this is more a tech demo than a serious implementation

I hope it helps
Monster

selection.py


import bge

PROPERTY_SELECTED = "selected"
PROPERTY_PROPERTY_NAME = "propertyName"

selectedObject = None

#--- bge callable
def selectHitObject():
    if not allSensorsPositive():
        return

    hitObject = getHitObject()
    
    selectObject(hitObject)

def selectOwner():
    if not allSensorsPositive():
        return

    owner = bge.logic.getCurrentController().owner
    
    selectObject(owner)

def selectByKeyProperty():
    if not allSensorsPositive():
        return
        
    owner = bge.logic.getCurrentController().owner
    propertyName = owner[PROPERTY_PROPERTY_NAME]
    keyCodes = getJustPressedKeys()
    for keyCode in keyCodes:
        character = bge.events.EventToCharacter(keyCode, False)
        mappedObjects = [object for object in owner.scene.objects
                        if object.get(propertyName) == character]
        for mappedObject in mappedObjects:
            selectObject(mappedObject)
                        
    
#--- selection business
def selectObject(object):
    global selectedObject
    deselectSelection()
    selectedObject = object
    object[PROPERTY_SELECTED] = True

def deselectSelection():
    if selectedObject:
        selectedObject[PROPERTY_SELECTED] = False
    
#--- bge helpers
def allSensorsPositive():
    for sensor in bge.logic.getCurrentController().sensors:
        if not sensor.positive:
            return False
    return True    

def getHitObject():
    for sensor in bge.logic.getCurrentController().sensors:
        try:
            return sensor.hitObject
        except AttributeError:
            continue

def getJustPressedKeys():
    pressedKeys = []
    for sensor in bge.logic.getCurrentController().sensors:
        try:
            events = sensor.events
        except AttributeError:
            continue
        for event in events:
            if event[1] == bge.logic.KX_INPUT_JUST_ACTIVATED:
                pressedKeys.append(event[0])
    return pressedKeys

Attachments

MultiObjectTest_revised.blend (449 KB)

A word of advice from an ancient being: when someone writes a piece of software that doesn’t do what it was meant to and asks “why?”, posting alternative solutions is not the right answer, you have to explain to the user why his code doesn’t work otherwise he will be left with the odd feeling that there is something magic going on.
And don’t be rude, the world is full or surprises. I give you one.
As it turns out, the code written by jeloco is a beautiful example of procedural programming. It is such a clean application of that perspective that I’d be frankly surprised to know he has no background in programming.
You see that because the program is entirely represented by branches, loads and stores.
Well, enough chatter.
The problem is that the lock status of the boxes is set to False whenever the mouse exits the current target. The block of instructions that does that is the third one, the “else”.


elif: #resets the lock out booleans
    print("lock one reset")
    own["lockTwo"] = False
    own["lockThree"] = False
    own["lockFour"] = False

One solution is to condition that state transition to a “mouse released” event:

...
leftReleased = bge.logic.KX_INPUT_JUST_RELEASED == mouse.events[bge.events.LEFT_MOUSE]
...
if own["lockOne"] == False:
    ...
    elif leftReleased:
        ...

That keeps the mouse-focus state of the block until not only the mouse is moved away from the object but the mouse button is also released.
Depending on the intended behavior, that condition might need to be expanded to also track transitions of the keyboard keys even though they seem to be disjointed in the original code.

Not a surprise beocuse it’s not true. First of all assuming a programming paradigm with just two functions is not realistic. If conditions are supossed to be branches and properties stores and you made your point based on that we can just stop here becouse it’s pointless. There is simple way to determine how well encapsulated his code is, try adding a button, how much effort you need to do that? In a properly encapsulated code it should take you one instruction / call / click and no more.

I do not understand your reply. I can’t imagine some code that is not there and use that to make a statement. That code is consistently procedural.
I’m not aware of any view of conditions as other than branches: they do create a branch in the flow of control.
The benefits of encapsulation are certain but not pertinent, since the procedural perspective requires no encapsulation - the first time we can start loosely talking about it probably comes with modularity and that’s twenty years after the procedural model.
I stand by my point that the code is not “shitty” at all, it is a beauty. With consequences, but still a beauty.

1 Like

@pgi

Beautiful or whatever, with such consecuences, it’s still bad code. And again, two functions is not enough to determine the programing paradigm, and following a programing paradigm doesn’t guaantee good code neither.

Thanks for suggestions you put up there. Let me comment on them.

@ pgi

If I use the mouse “just released” method you suggested then I have to do the same with the keyboard as well which means
more codes including a much bigger list of the KX stuff.

@ elmeunick9

Your code seem to do the trick but there is one problem. Whenever I click a cube then drag the cursor off and then back on,
the cube still lights up, but it didn’t when I don’t click on one to begin with. Plus I want to use the keyboard as well.

@ Monster

Your version doesn’t lock out the other keys whenever I mouse click or key press a cube.

As I said above, the only solution I’m looking for now is to lock out the cubes when I mouse click off of them.

mine should be better :cool: (:p)
…unfortunately with keyboard become a bit long

PS: im forget to give a name decent to the module.

Attachments

MultiObjectTest47.blend (74.8 KB)

jeloco, if you’re looking for a replacement to your script I think it would be best to provide us the exact requirements, the program you gave us might have unintended effects (for example keyboard and mouse interactions are not mutually exclusive and now I’m not sure if that is intended or not).
Some existing application matching the desired result can also work.
For example, are you trying to make a set of on-off exclusive switches, a button group of toggle controls like this:

http://demos.jquerymobile.com/1.4.0/controlgroup/

I’m still trying to work things out myself. What I have here will be buttons to activate things by either clicking on it OR by pressing
a key. I have no issues with the keyboard part of this sample as I have them working the way I have intended. Locking out others
while a key is down. I have looked at the versions posted above and taking them into consideration.

Here I’ll post a simple one cube sample where it will only light up by a mouse click so hopefully we’ll be able to find a solution
to keep the cube from lighting up by clicking off of it and drag the cursor on it.

Attachments

MouseOverTest1.blend (465 KB)

i guess to have the right one (as behaviour)
is a bit dirty but i fear that cannot be much more clean .
as suggested from Monster, the selector (an empty) do its internal stuff (using mouseOverAny, leftmouse and some flags)
but then “shoot” (delegate) the results to the cubes more directly as possible without make other operations (as change color or other)
in a way that the cubes get 2 boleans , “selected” and “activated”

Attachments

MultiObjectTest48.blend (75.3 KB)

Ohh well I thought that was what you wanted, here you have an improved version with Keyboard and what I suppose its what you asked. As always you can add a button with just duplicating one of the already present and it will work fine. I commented the code for clarity and to be noob-firendly.

MultiObjectTestLOLLIPOP.blend (439 KB)