Last week I shared my decision—in the name of more fun games for the world— to make good use of my commute time and build a Amazon Fire TV and Fire Stick game using GameMaker: Studio.
The GameMaker documentation gives a general overview of supporting game controllers. Finding details on how game controller support works with Amazon Fire TV proved to be a little more difficult. In the next few posts in the series I will provide the missing information, focusing on:
Let’s dive into basic controller detection.
Using YoYo Games GameMaker: Studio to build PC games that use the keyboard or even a USB game controller is pretty easy and many developers choose to first get their game up and running on their PC. This offers the convenience of easy debugging and very fast edit-build-test cycles. When you want to move to a mobile device or a platform like Amazon Fire TV, however, you are going to have to invest some time to build in proper support for game controller detection.
GameMaker provides a “System” event on objects. While it may do more in the future, currently the only thing this provides is game controller detection through async functions. This means that whenever a game controller is connected or disconnected, if you have a system event on an object, that code will run.
You don’t need to put this code on every game object. Typically, a game has a title or menu screen, which in GameMaker would be implemented as a room. Additional play screens are implemented as additional rooms. It is also common that in each of these rooms you will have a controller (not to be confused with the game controller) or state management object that lives the entire time the room is active. Often this is the object that will do things like draw the GUI for the game with the score, health, lives, etc. This is the object where you should include the code to detect the controller.
The System event returns a ds_map structure of name/value pairs. This will contain basically two pieces of information whenever a controller is connected or lost: which controller ID was affected (called the “pad_index”) and what happened, with the key “event_type”. The event_type will be either “gamepad discovered” or “gamepad lost”.
On Amazon Fire TV, this ds_map will also return information for the remote control, so you will need to take this into account if you want to support using the remote in your game or if you specifically do not want to support the remote.
In my single-player game, I created a global variable for the controller the player had selected to use. For a multiplayer game, you’d need to track this in multiple variables or an array and provide a means for each user to select their controller.
In the “Create” event for the state object on your first interaction screen (menu, title, etc.), add this code:
for (var id = 0; id < 10; id++;) { player[id] = noone; }
This code creates an array of 10 elements to hold controller IDs and sets all the elements to the built-in GML value representing nothing. Support for 10 devices is far more than you would typically ever find connected to an Amazon Fire TV, but better to support more and not have the possibility of a crash in the extreme case.
In the “System” event of the same object, add this code to detect the controllers being added and removed. This code will end up populating our player array with an element for the remote control as well as for each controller.
switch (async_load[? "event_type"]) { case "gamepad discovered": // get the value associated with key pad_index var pad = async_load[? "pad_index"]; // set the deadzones for the axis and analog buttons on the controller // as the code is written this will be set for the remote control // also, but will be ignored, so no harm gamepad_set_axis_deadzone(pad, 0.5); gamepad_set_button_threshold(pad, 0.1); // if the controller slot is already in use, assign the pad ID if(!instance_exists(player[pad])) { // *1* // set the selected gamepad to this pad global.gamepad=pad; with (player[pad]) { pad_num = pad; } // *2* } break; case "gamepad lost": // when a controller disconnect, clear its slot in the player array var pad = async_load[? "pad_index"]; if (instance_exists(player[pad])) { player[pad] = noone; } break; }
If you want to only detect game controllers without remotes, add this line at “// *1*” above:
if (string_pos("ontroller", gamepad_get_description(pad)) !=0) {
And a closing brace at “// *2*”
}
This code simply ignores any controller connection that doesn’t contain “ontroller” in the controller description.
Stay tuned for next week’s installment where I take a close look at handling controllers.