Programming Game Controllers in iOS 7

Post by

The iOS platform has been somewhat handicapped when it comes to games due to the twin stick interaction paradigm prevalent on games consoles. Even having physical buttons that give tactile feedback about what control the player has used existed on the first Atari consoles which hard glass screens cannot recreate.

On screen twin stick controls have improved over the years, but still don’t offer the responsive feedback of dedicated hardware controls, and will always have the problem of obscuring part of the user interface and the action going on in the game.

All this has changed in iOS7, with Apple quietly announcing the Made For iOS (MFi) Game Controller specification at WWDC 2013 and the first three controllers having come to market 7 months later.

The controllers come in two configurations. The standard controller has four face buttons, a d-pad, two shoulder buttons and a pause button while the extended controller has the same with two additional analogue sticks and two additional shoulder buttons. They can be either wrap around controllers with a lightning connection to the device or some extended controllers only connect via Bluetooth, which can also be used on OSX 10.8.

Apple forbid exclusively game controller interfaces, but they should still be prime candidates for feature inclusion in games as they will offer an excellent experience to the player.

In this post there will follow a quick rundown of the framework, followed by a guide on how to connect devices and get using them. This was written using the Logitech Powershell as a test device, which I cannot recommend as a device for development – nor any hardwired controller – due to the fact that you can’t live debug particularly easily. More on that later.

You will also find an Xcode project included that focuses on having a single controller for a game shamelessly ripping off Thomas Was Alone using UIDynamics exactly how Apple told us not to – as a physics engine for a game.

Framework Rundown

When it comes to the code, Apple have standardised the way you can interface with these controllers by creating the Game Controller framework. The entry point is GCController whose static methods and notifications provide discovery and disconnect handlers, while the instance methods expose details about the controller and the game pads themselves.

The game pads hold references to each button, directional pad and analogue stick. Analogue sticks and d-pads are implemented by the same class GCControllerDirectionPad and consist of two axes of input, but they can also be used as two pairs of exclusive buttons for up and down, or left and right. Each button, stick, and d-pad, plus all their sub components like axes, have block properties called valueChangedHandler.

Even the game pad and extended game pad objects have these. They are used to observe state changes for each component. When a button is depressed or released, or the sticks are shifted slightly, each handler from the button up through the collections of inputs to the entire game pad are executed. The pause button is an outlier in that it is the only control to have the handler block appear directly on the controller object and has no other representation.

Rather than event driven interaction you can use the game controllers to read the current state of input. If you have your own animation / game logic run loops, you can read the controllers’ state directly from the value properties of the elements.

Each controller that is discovered via bluetooth can be assigned a player index from 0 to 3 which should represent which avatar they control in your game environment and that value is reflected on their bluetooth controller light array.

Discovery

The first point of call for game controllers is finding out if the user has attached or paired one already. Any attached or paired controller will appear in the array [GCController controllers].

If that array is empty, how do we find controllers? The GCController class notifications and static methods for bluetooth discovery have us covered.

The completion handler on the wireless discovery method is called when “no more devices can be found”, you have called [GCController stopWirelessControllerDiscovery], or the method times out – you cannot assume a device has been discovered when the block is called. Given that each wireless controller being connected generates a connection notification, the block is there to update your UI when all devices are connected.

Subsequent calls to startWirelessControllerDiscoveryWithCompletionHandler will override the previous calls blocks, so only the most recent block that is sent to that method will be executed. You can safely call stopWirelessControllerDiscovery any amount of times and the completion block will only fire the once if it is relevant to do so – the block gets niled out after execution it seems.

The notifying object for connection and disconnection notifications is controller in question. You can retrieve it that way to determine which controller has been connected and set it up or tear it down as appropriate.

Disconnecting

Why would the user disconnect their device? How DARE they? They’re going to stop playing your game! Or maybe not. The framework posts another notification – GCControllerDidDisconnectNotification – when a controller disconnects, which could occur as a user pops his device out of the controller shell or a bluetooth controller runs out of battery for example. When a controller is found (Either when you first start looking or after a controller is announced to be connected) this is a good time to start looking out for this notification.

Input handling

As mentioned earlier, each button on the controller, and each d-pad, their axis and each individual direction on the d-pad and analog sticks have callbacks. Once a controller has been connected, we can start attaching callback blocks. All controllers will respond as gamepads, while only extended controllers will return a value for the extendedGamepad property. Try and accommodate both controller profiles if possible.

The results of the last two blocks in the above example for the dpad and the dpad xAxis will fire at the same time if the user presses the left or right buttons. The dpad values are analog, so have continuous values between -1 and 1 to represent left and right being pressed. If we were to use dpad.left the button values range from 0 to 1.

Callbacks are executed on the main thread, so be careful about what code you execute inside them.

Some actions in our game – in this case jumping – are conditional on the button having been released before it can successfully trigger an event in the game again. We can get this effect by tracking the last button state as it gets called.

Debugging

In writing this tutorial I was working with a Logitech Powershell. This wraparound controller has a built in Lightning connector to communicate with your iPhone or iPod Touch. As well as the fact that you can’t use this on an iPad or OSX, this is a royal pain in the arse for debugging. To plug the controller in, you forfeit your runtime connection to Xcode. I got around part of this by using NSLogger, a remote logging utility. Spark Inspector also helped monitor the notifications being thrown around the application at run time, even when not connected to Xcode.

There’s no way to connect a controller to the simulator. It may be the case that a bluetooth controller will find the simulator, but I doubt it. Bluetooth controllers are more diverse for use, and more powerful for debugging. I would recommend them in comparison to a wraparound controller for development purposes.

Let’s charge things up – a common game element is to hold down a button to build power that can reach a certain level and then unleash a strong ability. Using a date defined outside the block we can determine the length of time a button has been held down in order to gauge how long it has been depressed. Finally, we can read the value of a button directly off the game pad on release. This kind of technique might be used in sports games to power up a shot up to a maximum strength and apply spin by reading a thumb stick or d-pad value.

In the example project, you can see a slightly different approach that uses an NSTimer to update the visuals of the game as well as calculate the time since the button was first depressed. If you use a draw loop, you should instead store the NSDate in a property and calculate the duration that the button has been depressed – CADisplayLink would be equivalently effective for UIKit.

Code Architecture

It is very easy to write game logic straight into the controller handlers. I have tried to avoid this in the examples and in the sample project; given that the controller is not supposed to be the sole input method, the behaviour in the game controller handlers should be as thin as possible and call methods that are also called by equivalent inputs available for all iOS devices. If you don’t follow this pattern, you could easily end up with duplicated code handling game interface and logic. I also split the responsibility of discovery and input into separate classes so that input from a different interface, such as a set of UIButtons, could be configured with the same or appropriately similar behaviour.

In Closing

The Game Controller framework is very clean and concise, while the controllers make many games significantly more playable. Cross platform 3D engine Unity now supports MFi controllers, so Unity games have the option to use them and should start very quickly as can be cross platform. Apple have a habit of favouring apps that implement their new shiny features, so it’s only a matter of time as MFi Game Controllers start getting their own bucket in the App Store.

Further Reading


Tim Chilvers is a Lead iOS Developer at Future Workshops in London, obsesses about interaction patterns in video games and heckles his friends from Twitter (@Chilvman).
  1. In your examples, you check to see if the controller has the gamepad profile, and if not you check if it has an extended gamepad profile. Since Apple requires all extended gamepads support the normal gamepad profile, the extended gamepad methods in these cases will never be called.

    • You are correct, thank you Riley. I just found the line in the Apple Doc

      “If your game only requires a limited number of controls—the controls found in the gamepad profile—then use only the gamepad profile. The extended gamepad profile is a superset of the gamepad profile, and all devices that support the extended gamepad profile are guaranteed to support the gamepad profile.”

      I’ve asked to alter the article, and when I get my hands on some more hardware I’ll probably follow up with some more detailed posts.

Comments are closed.