Game Controllers

In Corona, creating apps with gamepad/joystick support is both easy and interactive. This guide outlines how the game controller APIs and events work together.

Requirements

Querying Devices

The initial API to begin with is system.getInputDevices() which returns a list of all detected input devices. Note, however, the following differences between platforms:

Essentially, when an application starts, you can check if key bindings for a device are stored:

local inputDevices = system.getInputDevices()

for i = 1,#inputDevices do
    local device = inputDevices[i]
    print( device.descriptor )
end

Effectively, each entry in the list returned by system.getInputDevices() is an instance of InputDevice. You can query this for information about the device, for example:

Connecting/Disconnecting Controllers

The inputDeviceStatus event is fired whenever a controller is connected or disconnected. The following example shows how to handle this event:

local function onInputDeviceStatusChanged( event )
    
    if ( event.connectionStateChanged ) then
        if ( event.device.isConnected ) then
            -- Device has been connected
        else
            -- Connection has been lost
        end
    end
end

Runtime:addEventListener( "inputDeviceStatus", onInputDeviceStatusChanged )

Within the listener function, the event table contains the following properties pertaining to the device which received a status change:

Handling Controller Input

Game controllers produce two types of events as a result of input: axis and key.

A key event's event.device property will be nil on both macOS and Windows. It can also be nil on Android if it came from a virtual input device such as the virtual keyboard.

The following example shows how to detect a specific controller. When either an axis or key event is triggered, the setDevice() function is called which sets the event.device and InputDevice displayName.

local controller = { device="", displayName="" }

local function setDevice( device, displayName )

    -- Set current controller
    controller["device"] = device
    controller["displayName"] = displayName

    -- Remove event listeners
    Runtime:removeEventListener( "axis", onAxisEvent )
    Runtime:removeEventListener( "key", onKeyEvent )
end

local function onKeyEvent( event )
    setDevice( event.device, event.device.displayName )
end

local function onAxisEvent( event )
    if ( math.abs(event.normalizedValue) > 0.5 ) then
        setDevice( event.device, event.device.displayName )
    end
end

Runtime:addEventListener( "axis", onAxisEvent )
Runtime:addEventListener( "key", onKeyEvent )
Notes
  • In the above example, event.normalizedValue for the onAxisEvent() listener function is compared to 0.5. This means that the axis is turned by a "readable" amount, or more than halfway through.

  • Some game controllers produce both axis and key events when analog triggers are pressed, so you may wish to conditionally handle this possibility.

Handling Keys

Keys are handled with key events. Whenever the user presses a button on a gamepad/joystick or a key on the keyboard, this event is triggered. To identify which button/key was pressed, refer to the event.keyName documentation. Furthermore, when a key event occurs, event.phase will equal "down" when the button/key is pressed down, or "up" when the button/key is released.

Handling Axes

The axis event fires any time the controller's axis values change. This can result in a large amount of events, so game developers should consider accumulating the axis state. Each event has several useful properties:

Important
  • The usual workflow when dealing with axis events is to accumulate values. In this approach, event.normalizedValue is typically most useful.

  • Like devices, an axis can provide useful information about itself. InputAxis tables are returned by object:getAxes() and axis in axis events. Probably the most useful property is type which attempts to describe the utility of the axis.

Summary

References

Documentation

Sample Code