Request: bge.events.JOY1BUTTON1

If you haven’t already infered from the title, I’d like to suggest an addition to the bge.events list.
In particular, I’d like to see joystick buttons added there, as it seems logical that they would be there.

Things like:


bge.events.JOY1BUTTON1
bge.events.JOY1BUTTON2
...
bge.events.JOY1HATUP
bge.events.JOY1HATDOWN

There are a maximum of eight joysticks, each with an indefinite number of buttons and hats, and this the API is an indeterminate size. This is why it isn’t already there?
But: There is the API for the mouse there even if one isn’t plugged in, and even if it doesn’t have a scrollwheel.

So I see no reason why you can’t accommodate for all possible buttons, hats, axis etc. Or is the joystick protocool so good that a joystick can have an infinite amount of hats and buttons? In which case, sensible values can be drawn, such as a maximum of 5 hats and 30 buttons are supported by blender. Something that will never sensibly be exceeded. This will make the documentation huge, except that you can shorten it to say that these are the numbers that change under these situations. No need to list every case.

Why I want it implemented:
To standardize. Making a keymapper is ridiculously easy for a moderately good programmer, by creating a dict of ‘forwards’:‘WKEY’, ‘shoot’:‘LEFTMOUSE’, and some relatively simple code (EventToString(event) mainly). This is perfect for mouse and keyboard, but does not work for joystick because it is completely different, and requires a whole bunch extra code specific to joysticks.

Also, mouse button 4 and 5 would be great

I made a key-mapper / input handler that works with joystick buttons and axes, key presses, mouse axes and buttons, and has “isPressed() / isDown() / isReleased()” style handling for them as well. It also supports naming (i.e. “walkleft” being bound to holding down left on the left joystick, as well as the left keyboard key). It’s called BGInput. I also have another module named JoyProfile that has some constants in Python classes to be used “static-style”. Using the modules together are pretty simple:



import BGInput, JoyProfile

cont = logic.getCurrentController()
obj = cont.owner

if not 'init' in obj:
    
    obj['init'] = 1
    
    obj['inputdev'] = BGInput.CInputDevice()    # Make a new input device instance; only needs to be one existing at a time to handle all input.
    
    jp = JoyProfile.Xbox360 # Refer to the XBOX 360 (X-Input) controller layout
    
    obj['inputdev'].Add('left', BGInput.KEY, events.LEFTARROWKEY)   # Create a binding for pressing left

    obj['inputdev'].Add('right', BGInput.KEY, events.RIGHTARROWKEY)

    obj['inputdev'].Add('jump', BGInput.KEY, events.ZKEY)    


    
    obj['inputdev'].Add('left', BGInput.JOYAXIS, jp.Stick_LeftHori, axisdir = -1)   # Create a binding for pushing left the joystick, binding it to the same "left" binding

    obj['inputdev'].Add('right', BGInput.JOYAXIS, jp.Stick_LeftHori, axisdir = 1)   
 
    obj['inputdev'].Add('jump', BGInput.JOYBUTTON, jp.A)



obj['inputdev'].Poll() # Do this once per game frame, and you'll be able to check the input easily by just doing:



print ( obj['inputdev'].BindDown('left') ) # Print if the "left" key is down

print ( obj['inputdev'].BindReleased('right') ) # Print if the "right" key is released this frame

print ( obj['inputdev'].BindPressed('jump') ) # Print if the "jump" key was just pressed

You can get it by doing an SVN check-out on my Google Code project page. There’s an example blend file there as well.

I would still like for constants and press / release states to be added as well, however. It’d be nice and convenient.

I’m all for the proposal. It feels a bit silly that one has to evaluate SCA_PythonJoystick with a Python script to know if a direction on the axis, a button or hat is not pressed, just pressed, pressed or just released. I would suggest to use the same input status formula (bge.logic.KX_INPUT_NONE; bge.logic.KX_INPUT_JUST_ACTIVATED; bge.logic.KX_INPUT_ACTIVE; bge.logic.KX_INPUT_JUST_RELEASED); respectively values 0, 1, 2 or 3.

But since bge.events contains handles for the sensor, it’s not necessary to split into JOY1, JOY2, … The Joystick Sensor already defines which Joystick is being used (with the index value). The same with bge.logic.joysticks. As the name already suggests it’s a list of which each joystick can be accessed through indexing. So in order to know if button 1 of joystick 0 is just activated you would write this:

if bge.logic.joysticks[0].events[bge.events.1BUTTON] is bge.logic.KX_INPUT_JUST_ACTIVATED:
    ...

Edit: If there’s any interest in how to evaluate with Python, this is how I’m doing it:

class Input:

    buttons = {'forward': 2, 'reverse': 0, 'brake': 3, 'e_brake': 1, 'boost': 7, 'switch_camera': 6}
    
    def __init__(self):
        self.gamepad = logic.joysticks[0]
        if self.gamepad:
            self.buttons_status = {button_name: [True, False, False, False] for button_name in self.buttons}

    def get_gamepad_input(self, none=0, just_activated=1, active=2, just_released=3):

        def update_buttons(self):
            active_buttons = self.gamepad.activeButtons
            for button_name, button in self.buttons.items():
                button_0, button_1, button_2, button_3 = self.buttons_status[button_name]
                if button in active_buttons:
                    if button_0:
                        self.buttons_status[button_name][none] = False
                        self.buttons_status[button_name][just_activated] = True
                    elif button_1:
                        self.buttons_status[button_name][just_activated] = False
                        self.buttons_status[button_name][active] = True
                elif button_2:
                    self.buttons_status[button_name][active] = False
                    self.buttons_status[button_name][just_released] = True
                elif button_1:
                    self.buttons_status[button_name][just_activated] = False
                    self.buttons_status[button_name][just_released] = True
                elif button_3:
                    self.buttons_status[button_name][just_released] = False
                    self.buttons_status[button_name][none] = True

        update_buttons(self)
        forward_2 = self.buttons_status['forward'][active]
        _, reverse_1, reverse_2, reverse_3 = self.buttons_status['reverse']
        _, brake_1, brake_2, brake_3 = self.buttons_status['brake']
        _, e_brake_1, e_brake_2, e_brake_3 = self.buttons_status['e_brake']
        boost_1 = self.buttons_status['boost'][just_activated]
        switch_camera_1 = self.buttons_status['switch_camera'][just_activated]
        ...