Adobe AIR GameInput pitfalls

Adobe AIR’s new GameInput APIs are great. They provide seamless, powerful input from game pads for Android, desktop and web applications created in Flash.

However, it has two important problems right now. Since those warrant some explanation, I figured I’d make a post about it hopes it’ll help other people working with those APIs.

The first one is that merely listing the number of connected GameInput devices prompts the creation of an internal Timer that runs every second. This timer is used to constantly check for the addition of new devices, and for dispatching the device addition or removal events when needed. The problem with this timer is that it adds some massive overhead, frequently going way over the execution time budget a frame would have. The following Adobe Scout screenshot shows a SWF with no code other than the GameInput listener activation spending 38ms on the device checking timer.

Scout screenshot of a SWF with GameInput on

Scout screenshot of a SWF with GameInput on

This bug only exists on desktop versions of Adobe AIR; the Android/OUYA version listens to system-provided listeners on device changes, and therefore there’s no impact on performance. However, 38ms is still 228% of the frame budget of a game running at 60fps, and therefore unacceptable. Even for well programmed, time-based games, skipping 2 or 3 frames every second is really disconcerting.

There’s no workaround for this as of now. Adobe is aware of the problem and is working on a fix (AIR 3.8 is in beta after all).

Update (September 15, 2013): this problem has apparently been fixed in the newer versions of Adobe AIR. There’s no slowdown visible and Adobe Scout shows no slow Timer event at all when using AIR 3.9 beta 790 and Flash Player 11.9.900.93.

The second problem is that sometimes your game input will just not work on Android (and the OUYA). You’ll test a game that runs perfectly once, and then run it again without changes, and the game controllers won’t be recognized. They will never work again, until you reboot the device or disconnect and reconnect the controllers (deactivate and reactivate in case of OUYA’s own controllers).

This problem has confounded me for a while. Luckily, users shawnb81 and Ultima2876 over at Adobe’s forums found the issue: the GameInput instance that is created for listening to device changes must be created in the first frame of your application code (e.g. your SWF document class). This means that you cannot start listening to game input events after a while; game devices will be listed, but always return null when read.

On my KeyActionBinder, I’ve sort-of-solved this problem by creating the GameInput instance on a static initializer. This means that you don’t have to create any instance on your document class (it may be annoying to pass that along to the rest of your application), but you need to reference the class somehow to force the initialization. So, in practice, this means having this code on your first SWF class:

// Reference class to force early creation of GameInput listener
KeyActionBinder;

For reference, just accessing the class like this forces this code to run:

{
	if (GameInput.isSupported) gameInput = new GameInput();
}

That listener instance is then shared by all KeyActionBinder instances.