Mapping data to game controls

In the update() function, the information retrieved from gamepad and joystick events is used to refresh the application's state.

To determine the state of the analog sticks, we use the data retrieved by querying the SCREEN_PROPERTY_ANALOG0 and SCREEN_PROPERTY_ANALOG1 properties. Here, it's useful to note that the analog0 and analog1 members of the GameController struct are arrays of three integers and not two integers. For most game controllers, we're interested in the first two values only. These values correspond to the horizontal and vertical axis of an analog stick respectively, and range between -128 and +127, with a value of (0, 0) specifying that the stick is in its default position.

The third integer is used by some input devices that use the SCREEN_EVENT_JOYSTICK type, and is used to support analog triggers. For example, the MOGA Pro Controller includes two analog triggers, and you can use the third integer in analog0 and analog1 to retrieve the current value of each trigger. These values correspond to how far each trigger is pressed, and range between 0 (indicating that the trigger isn't pressed) and 255 (indicating that the trigger is pressed all the way). Other gamepads might include digital triggers, which don't have a range of values and instead support only two values (pressed and not pressed).

To determine the state of the buttons, each bit in the integer retrieved by querying SCREEN_PROPERTY_BUTTONS is decoded. Each bit is a flag representing a single button, which is set when that button is pressed down. The first twenty bits correspond to constants found in screen.h and these constants are mapped to buttons for the recommended controllers for this API. These constants are defined in an enumeration as follows:
enum {
	SCREEN_A_GAME_BUTTON                   = (1 << 0),
	SCREEN_B_GAME_BUTTON                   = (1 << 1),
	SCREEN_C_GAME_BUTTON                   = (1 << 2),
	SCREEN_X_GAME_BUTTON                   = (1 << 3),
	SCREEN_Y_GAME_BUTTON                   = (1 << 4),
	SCREEN_Z_GAME_BUTTON                   = (1 << 5),
	SCREEN_MENU1_GAME_BUTTON               = (1 << 6),
	SCREEN_MENU2_GAME_BUTTON               = (1 << 7),
	SCREEN_MENU3_GAME_BUTTON               = (1 << 8),
	SCREEN_MENU4_GAME_BUTTON               = (1 << 9),
	SCREEN_L1_GAME_BUTTON                  = (1 << 10),
	SCREEN_L2_GAME_BUTTON                  = (1 << 11),
	SCREEN_L3_GAME_BUTTON                  = (1 << 12),
	SCREEN_R1_GAME_BUTTON                  = (1 << 13),
	SCREEN_R2_GAME_BUTTON                  = (1 << 14),
	SCREEN_R3_GAME_BUTTON                  = (1 << 15),
	SCREEN_DPAD_UP_GAME_BUTTON             = (1 << 16),
	SCREEN_DPAD_DOWN_GAME_BUTTON           = (1 << 17),
	SCREEN_DPAD_LEFT_GAME_BUTTON           = (1 << 18),
	SCREEN_DPAD_RIGHT_GAME_BUTTON          = (1 << 19),
};
In the Gamepad application, the mapping of physical buttons on the device to virtual buttons on the screen is stored in the _buttons[] array, which is of type Button.
typedef struct Button_t {
    // A button's type determines which set of UVs it uses.
    ButtonType type;
    int mapping;
    Quad* quad;
    char* label;
} Button;
In the init() function, the mapping member for each Button is assigned one of the flags in the above enumeration to uniquely identify the button. This member can be re-mapped at any time by tapping a virtual button on the screen of the BlackBerry 10 device and then pressing the corresponding physical button on the gamepad.

It's highly recommended to provide similar re-mapping functionality in your app, not only to support devices that don't have mappings in the Screen and Windowing Library but also to allow users to choose their own controls.

This code determines whether a particular button is being pressed on a specific controller:
if (controller->buttons & button->mapping) {
    ...
}
Here, controller->buttons is the integer retrieved using screen_get_event_property_iv() and SCREEN_PROPERTY_BUTTONS and button->mapping is a single value from the enumeration above.
To check a single button at a time, rather than looping through a list of buttons as in the sample, you would do something like this:
if (controller->buttons & SCREEN_A_GAME_BUTTON)
{
	/* The A button is currently down. */
}

if (controller->buttons & SCREEN_B_GAME_BUTTON) {
	/* The B button is currently down. */
}

if ( ... ) { /* etc. */ }
We check each button individually as it's possible that more than one button is being pressed.

Other considerations

When creating default button mappings for your game, it's important to remember that some controllers can be used in either portrait or landscape orientation, such as the Wii Remote. This means that buttons that may be easy to reach in one orientation could be difficult to reach in another. For example, the A and B buttons for the Wii Remote are mapped to the A and B buttons in the buttons enumeration but these are difficult to press when holding the controller in landscape orientation. You can make this easier for the player by mapping the X and Y buttons on the Wii Remote to A and B in the buttons enumeration, respectively.

While analog sticks provide higher precision in movement, not all game controllers have them. To accommodate game controllers without analog sticks, consider designing your app to use the D-pad to perform the same actions.

For game controllers that include trigger buttons (for example, the analog triggers on the MOGA Pro Controller), consider the type of trigger that's available and how you want to handle trigger input in your game. A controller might include digital triggers, which work like other buttons on the controller and support only two values (pressed and not pressed). Or a controller might include analog triggers, which support a range of values and can be partially pressed. A controller will likely include either digital triggers or analog triggers, but typically not both, so you should keep this in mind as you design your game.

That's all there is to it! The rest of the sample is dedicated to rendering graphics and re-mapping controls, which isn't necessary to understand the functions we've gone over above. Feel free to browse the code, change things up, and modify the app to try something different.

Last modified: 2014-05-14



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus