Accessing Sensors/Controllers through python

I know that there are ways to do this, just wondering if anyone could give me insight on how.
RIght now Im using keyboard sensors by attaching a sensor and defining it like so
aKey = cont.sensors[“keyboard”]

how would i go about not doing this ^ and using bge.logic.keyboard.events[bge.events.AKEY] without attaching any sensors. is this same possible with controllers? Would i need to use a sensor if i wanted to specify things like tap or pulse on a mouse, or property type on a near, etc?

Skipping using sensors means you skip the complete build-in event system.

This means you need to implement an own polling system.


from bge import logic, events

keys = logic.keyboard

A = logic.KX_INPUT_JUST_ACTIVATED == keys.events[events.AKEY]       #"tap" key control

if A:
    doStuff

That is how I do keyboard input without logic bricks. The above code will work like the ‘Tap’ option, only registering true for the instant the key is first pressed down. You can use KX_INPUT_ACTIVE or KX_INPUT_JUST_RELEASED as well for testing if the key is being held down, or being released (anti-tap?)

neat! thanks!

Hi! @Nines: It’s weird, but tap send 2 pulses (1 when sensor is just activated and 1 when sensor is just released)…
I’ll see if there is a reason for that and try to make a patch to change that (send only one pulse if sensor is just activated).

http://www.pasteall.org/blend/37558

It seems that it’s this part of the code in SCA_ISensor.cpp:

if (m_tap)        
{
    // in tap mode: we send always a negative pulse immediately after a positive pulse
    if (!result)
    {
         // the sensor did not trigger on this frame
         if (m_prev_state)
         {
               // but it triggered on previous frame => send a negative pulse
               ActivateControllers(logicmgr);
               result = true;
         }
         // in any case, absence of trigger means sensor off
         m_state = false;
     }
}

I’ll try to understand…

if you change the code like this:

if (m_tap)        {
     // in tap mode: we send always a negative pulse immediately after a positive pulse
     if (!result)
     {
         // the sensor did not trigger on this frame
         //if (m_prev_state)
         //{
                 // but it triggered on previous frame => send a negative pulse
                 //ActivateControllers(logicmgr);
                 //result = true;
         //}
         // in any case, absence of trigger means sensor off
         m_state = false;
     }
 }

and you execute print(bge.logic.getCurrentController().sensors[0].status) it prints only “1” (just_activated). I’ll ask the devs what it could involve.

EDIT: It’s preferable to let the user choose beetween just_activated or just_released states. Controllers and actuators can (well should) handle both positive and negative events. Two pulse are correct. If tab enabled and the sensor is activated it will send in the first frame an activation and in the second frame the deactivation, even if the sensor stay activated. That’s the answers of the devs.

Hi, Is there any way to get what keys you’ve just pressed with a simple check? Like the code below

pseudo


for x in keys.events:
    if x == bge.logic.KX_INPUT_JUST_ACTIVATED:
        print(x)

I think it should also include KX_INPUT_DOUBLE_TAPPED as well, double tap/click is a very common input

This is nice :smiley:

but will the list always return up, left, shift, if your holding it?

(every time?) (serialized list?) or does the order change ?

@@BluePrintRandom, if you use All Keys and print(sensors.events), holding down multiple keys will return multiple lists[Ascii, keystatus], from 2~6 lists depending on the key combination, so its a serialized list that order themselves based on your input

I was looking for a way to do this without using the keyboard sensor

Hi. @guramarx: I’m not sure this is what you are looking for but you have this: http://www.blender.org/api/blender_python_api_2_75a_release/bge.types.SCA_PythonKeyboard.html?highlight=active_events#bge.types.SCA_PythonKeyboard.active_events
http://www.blender.org/api/blender_python_api_2_75a_release/bge.types.SCA_PythonMouse.html?highlight=active_events#bge.types.SCA_PythonMouse.active_events

It returns a dictionary containing keycode (http://www.tutorialsforblender3d.com/GameModule/KeyCodes.html , http://www.blender.org/api/blender_python_api_2_75a_release/bge.events.html?highlight=event#bge.events.EventToString) as key, and key status (1, 2, 3 >just_activated, active, just_deactivated) as value.

@youle, Thanks that is exactly what I need

Yes,
bge.logic.KX_INPUT_JUST_ACTIVATED

is an int, as are all the constants for that status.
So you don’t need to use the full title, just use 1, 2 or whatever.

Here’s a simple function for checking for keyboard input.

def key_triggered(key,tap = False):  
    
    if tap:
        triggered = [1]        
    else:
        triggered = [1, 2]
           
    if bge.logic.keyboard.events[key] in triggered:
        return True
    
    return False

space key = 32 (you can get the numbers which relate to the keys here.)

So I would check for space being pressed by using:

space_is_pressed = key_triggered(32,tap=True)

or I’d use a custom keys dictionary:

default_actions =  {"action":(113,"QKEY",True),
        "show_dialogs":(101,"EKEY",True),    
        "jump":(32,"SPACEKEY",False),
        "flashlight":(102,"FKEY",True),
        "crouch":(124,"LEFTCTRLKEY",True)}   

active_keys = []

for key_key in default_actions:
    key_data = default_actions[key_key]
    
    check_key_triggered = key_triggered(key_data[0],tap=key_data[2]) 
    if check_key_triggered:
        active_keys.append(key_key) 


And that would give me a list of actions performed that tic, like [“jump”,“flashlight”]

Then I could do actions like:

if "jump" in active_keys:
    player_jump()

You can customize the keys pretty easily from there by editing the default_actions dictionary. Usually I have a default key setup, then I load that in to globalDict. From there it’s very easy to assign custom keys to the dictionary:


setting_string = "flashlight"
setting_key = globalDict['keys'][setting_string]

current_pressed_key = None                   
keys_pressed =  bge.logic.keyboard.events
    
for pressed_key in keys_pressed:    
    if keys_pressed[pressed_key] == 1:
        current_pressed_key = pressed_key  
        current_key_name = bge.events.EventToString(current_pressed_key)

if current_pressed_key:
    tap = setting_key[2]
    setting_key = (current_pressed_key,current_key_name,tap)

@@Smoking_mirror, WOOT! that is awesome and extremely helpful to me and saves a ton of time, at this point I was just testing things one at a time and have yet had any plan or thought of key mapping nor any idea on how to accomplish it lol

Its an interesting approach to key map controls, I’d never come up with a solution like that, I really appreciate this and will learn as much as I can, thanks

I actually use this blend for checking key numbers:
keymapping.blend (394 KB)

Since the reference page I posted above takes time to scroll through to find what I want, whereas with this I can just press the keys I need and write down the numbers. Press F12 to exit, as escape key is not set as exit.

It takes some time to set up this system, but once it’s done it makes getting keyboard input a snap. it also makes setting up custom key mapping really easy.

An alternative is to have several default keymap dictionaries already setup and then choose which one to use:

bge.logic.globalDict['keys'] = wasd_control
## or 
bge.logic.globalDict['keys'] = numpad_control

You can do something similar with mouse button input too:

def mouse_triggered(button,tap=False): 
    mouse = bge.logic.mouse
        
    if tap:
        triggered = [1]        
    else:
        triggered = [1, 2]        

    if mouse.events[button] in triggered:
        return True
    
    return False  

And here’s a similar blend for getting the mouse events codes:
mouse_mapping.blend (394 KB)

Awesome ! :smiley:

TAP is intended to send two pulses - it sends a negative trigger immediately after a positive one.

The reason is simply: That is how it should be.
Is there any reason to post that in this thread? I do not see a relation to the topic.

I strongly suggest to avoid the numbers from the above snippets. Better use the constants from the BGE API e.g. JUST_ACTIVATED or AKEY . Otherwise the code is hard-to understand. (Btw. the BGE API contains snippets too.)

@Monster: it was an answer to Nines about this line:

A = logic.KX_INPUT_JUST_ACTIVATED == keys.events[events.AKEY] #“tap” key control

The reason why “tap” is done like that was not evident to me. And it’s the same thing for many users (Many questions about why when I’ve a sensor keyboard “tap”->python script->property add 1, and in the python script I say: cont.activate(“property”) it adds 2 instead of 1 to the property).

This is because they still forget to check the sensor status :). “with great power. comes great responsibility.” - spiderman movie

As thelaurent abstain from the benefits of the build-in event system, the Tap configuration is not valid in this situation. His code has to run all the time.

Nines comment is a little bit misleading as there is no sensor configuration to check and therefore no tap option. I guess he want to compare it to the sensor settings (which does not really fit). KX_INPUT_JUST_ACTIVATED describes the situation pretty well (when you know there is a KX_INPUT_ACTIVATED) .