Big changes to KeyActionBinder: automatic game control ids, new repository

When I set out to write a class to simplify game input control in ActionScript 3, I started from the assumption that Adobe AIR’s GameInput device control ids (the ids that relate to each button or axis) would be fairly consistent between platforms. The idea was that the class would just hide away the Keyboard and GameInput events needed for proper game input, and not try to do much else.

As it turns out, that assumption was a mistake. Now that more people are testing GameInput control, it has become obvious that control ids vary immensely between platforms, even for the same device (e.g. the XBox 360 Controller has completely different control ids triggered on Android, Windows, and Macs). The spreadsheet with all known game control ids is getting out of hand. In retrospect, I suppose this is to be expected – each platform will have its own device drivers, sometimes not necessarily maintained by the controller manufacturers.

In my original implementation of GameInput support for KeyActionBinder, I had decided to have all GameInput control ids listed in a separate class, and then it was up to developers to properly bind the correct controls based in which platform the code was being executed on.

While this works, it’s a very verbose approach and one that will probably have very little – if any – customization over time (e.g. “left stick” will always be treated as “left stick”, regardless of the platform). Over time, I came to believe that a more universal, automatic, cross-platform approach with control ids hidden away from the developer – the solution James Dean Palmer took with his own GameInput classes – is a better solution.

So in essence, something that looked like this:

// Setup bindings
if (isAndroid) {
	binder.addGamepadActionBinding("move-left", GamepadControls.OUYA_DPAD_LEFT);
} else if (isWindows) {
	binder.addGamepadActionBinding("move-left", GamepadControls.WINDOWS_DPAD_LEFT);
} else if (isMac) {
	binder.addGamepadActionBinding("move-left", GamepadControls.MAC_DPAD_LEFT);
}

Should look like this:

// Setup
binder.addGamepadActionBinding("move-left", GamepadControls.DPAD_LEFT);

Checking the actual actions (via isActionActivated(), etc) would remain unchanged.

This may sound like an obvious, sound solution. The hidden implication, however, is that the class has to discover which platform and device are being used behind the scenes and bind events with the correct control ids where appropriate. This is not as easy as it sounds (certainly not infallible), because it requires prior knowledge of all possible platforms and devices (rather than leaving it to the user to add arbitrary control ids) and some creative guessing based on device names and player “manufacturer”/OS strings. Needless to say, devices have to be individually “supported”; if a player tries to use a new, random device in your game, it wouldn’t work by default, because the class has no knowledge of it.

Still, this is the kind of solution that I think will only get better over time. In certain platforms (at least Android and Windows via XInput), game input is more or less universal, and as long as the device recognition code is lenient enough, it can probably work reasonably well even in rogue new devices.

This is a big change to the code, so on top of that I’m also doing some other changes to the class. Those include adding game pad index parameters when checking for action activations (for a more flexible easier multiplayer support), some automatic keyboard-to-GameInput bindings (to support weird keys such as START or HOME that get mapped to Keyboard.MENU or Keyboard.BACK) and re-mapping of some axis values for consistency (so developers don’t have to care about the direction of the Y-axis on their sticks).

This is still a work in progress, but a working version of that (sans some of the features mentioned above, and only supporting a few devices and platforms) is up on GitHub: I have a new repository just for KeyActionBinder (easier for forking). The main branch still contains the original version of KeyActionBinder, and the new one – with automatic mappings – are on the auto-controls branch. Check the list of changes done so far for more information.

I will be adding the rest of the devices and features to it over the next few days, and publish my web/Android GameInput test SWFs so it’s easier to add more devices to it, as well as a full log of all the changes. In the meantime, this post is just to let people know there’s a new version that’s worth trying.

2 responses

  1. The autodetection is great, though I would think that would suit itself as a separate class not specific to KeyActionBinder? Assuming that the control mappings would be specific to an OS/Device that we’re targeting, wouldn’t we be able to provide that ourselves?

    You mentioned in an earlier post using a compile time constant CONFIG::PLATFORM, this could be passed to KeyActionBinder without the need for autodetection, or have I missed something that makes that no longer viable?

    Is there a reason you’re defining all the controllers as string objects and not creating controller classes that are provided mappings externally? You could then create an instance of a controller with something like:

    _gamepad = gamepadFactory.create(PLATFORM, GAMEPADNAME)

    Where the factory class based on the platform and gamepad name input could create a controller of that type and bind mappings for that platform-controller followed by returning the controller instance to KeyActionBinder?

    Could totally be misunderstanding what’s happening though.

    • Passing parameters: originally, I’d detect the platform myself and then add custom controls based on it. It worked well, and it tied well to the CONFIG::PLATFORM constant I always use in my own prototypes.

      However, it turns out there’s a massive number of different configurations possible, not just between platforms, but between devices and operating systems and operating system versions. It turns out that anyone wanting to do it manually have an uncanny amount of work to be done before a gamepad can be used (unless they’re targetting a really restrictive configuration, like say, OUYA only).

      My revised approach in that sense is using the automatic configuration. It has its own issues but I think over time it’s more beneficial to have that as part of the system. It allows the developer to focus on detecting the behaviors he/she wants rather than getting the whole system setup first.

      ***

      Right now it’s all in one single class. Eventually I’ll probably move that into a (invisible) separate class, but right now this approach works for initial development. Anyway, the reason it’s a single piece of code is that it’s easier to maintain a long list of JSON-like objects for filtering rather than creating a separate class for every device/OS/OS version combination. In the future it might actually be externalized into a JSON file that gets embedded. All in all it make tweaks and additions easier, IMO; it’s easier to parse it visually.

      ***

      The approach with a controller instance is different from what I want to do. Rather than creating controller instances that the developer has access to, or exposing new classes to the developer, I just want to create data that can be easily checked based on simple parameters. Both approaches are valid, and things might change, but this is the one I’m going with so far. Normally I like to test different approaches until I decide what works best, and this is just the 2nd “generation” of my key/button-action binding approach. So I can’t really say what’s better, just that I believe it fits better into what I’m developing.

      With that said, I’m pretty sure there’s other frameworks there with a different, more reference-based approach.

Comments are closed.