Polling interface is Bad

Unless I’m missing some hidden information not available in the manual and reference - the polling interface (base.mouseWatcherNode.is_button_down) is bad.

The length of the name is one thing:

if base.mouseWatcherNode.is_button_down.KeyboardButton.ascii_key(u'q'):
    self.do_jump()

There’s a different API for ‘normal’ keys KeyboardButton.ascii_key() and a different for ‘special’ keys KeyboardButton.space(), KeyboardButton.insert(), KeyboardButton.escape() and so on. That means if you want to have any sort of key configuration you need a dictionary to map space to KeyboardButton.space(), escape to KeyboardButton.escape() because you’ll be storing ‘space’ in a configuration file, not KeyboardButton.space().

It gets worse if you add mouse and controllers to the mix - there is no one way to watch for a key press that is common for all input devices. If you want to jump with the space you’ll watch for KeyboardButton.space(), if you want to jump with the mouse it will be MouseButton.three(), if you want to jump with a gamepad button… yeah, you got something like if self.gamepad.find_button("lstick").pressed: (after setting up self.gamepad first, and that’s another extra bit of code…).

Why is this all so needlessly complicated?
Using events you have self.accept(key_name, self.do_jump) and key_name can be a keyboard button (n) a special keyboard button (space) a mouse button (mouse3) it could be a controller key after some extra setup.

Coudn’t we just have a InputDeviceWatcher with a dictionary-like interface so that we could just do:

if base.InputDeviceWatcher['keyboard-space']:
    self.do_jump()
if base.InputDeviceWatcher['mouse-1']:
    self.fire_zer_missile('!')

and it should also work for analogue axis:

x_axis = base.InputDeviceWatcher['gamepad-x-axis'] #returns a value in -1.0, 1.0 range
self.ship.set_h(self.ship.get_h()+x_axis*dt*rotation_speed)

Sorry if this looks like a rant (it kind of is), but reacting to player action is what makes games games and it should be as simple as loading a model and parenting it to render, you shouldn’t need 200+ lines of obscure code just for watching if a key/button is pressed.

Oof–I haven’t looked at the polling interface, but I can see what you mean, I believe (and in addition I have looked into the setup for devices like gamepads). :/

For what it’s worth, I’m hoping to release a new version of my KeyMapper soon, which handles a lot of this stuff in the background. It has its limitations, and it doesn’t use the polling interface for anything other than axes, but it might be worth looking at when I release the new version.

It should support keyboard, mouse, and Panda’s additional devices, and keeps track of which inputs are assigned to which controls.

And while it’s not done by polling in the background, the “held-key” KeyMapper control-type is intended to be polled, so it may serve your purposes.

(And since I recall that you’re not a fan of my GameSaver module, you might be happy to know that KeyMapper no longer assumes that GameSaver is to be used–you can provide your own file-handling methods to save out and restore the data that KeyMapper uses to store its mappings.)

rdb over on discord explained to me that one can use base.mouseWatcherNode.is_button_down('space') or base.mouseWatcherNode.is_button_down('mouse1') so there is a common, sensible way to do it, just ‘that way may not be obvious at first unless you’re Dutch.’
But it’s just for mouse buttons and keyboard keys, you still need different code for controllers.
It may not be as bad as I said, but it’s still not good.

That is a little better, indeed.