GMS 2 How to use a GamePad?

I know that is a very vague question, but if we just assume that I have a completely empty project and I want to see if a specific controller is pressing the right trigger. I know that there is a way to make a 1D array of the controllers that are currently plugged in, is there a way to select the order of those controllers, ex. make it say, "Player 1 press (A)" and then put the controller that pressed at the top of the array? Also is there a way to see what kind of controller it is? If we continue with the last example that it says "press (A)" if it is an Xbox controller but it says "press (X)" if it is a Play Station controller... sorry if this is a lot to ask for but I haven't found any good tutorials online on how to set up a GamePad and I decided that instead of making one thread per question I could just make a makeshift "megathread" with everything needed to use a GamePad.

Thanks for ANY help at all.

Edit: I have been trying to understand now for a few hours, and I think I'm gonna lose it... (I am kidding, but I have been trying to get it to work for a long time by now...)
 

TailBit

Member
For setting the controller for each player .. have an array or something where you can store the controller index and its gamepad_get_guid(index) number, maybe name too ..

Then have a code the loops through all controllers, as shown in the example for gamepad_get_device_count(); and for each checked, then get gamepad_button_count(device) and loop through all the buttons checking for press .. if found then assign controller to that player and store the _guid and name

On normal gameplay you just loop through the gamepads you have assigned for the X number of players..

Now, there is a chance that one controller could lose connection and they will end up changing index as this happens, in async events you would want to make a code to check the connected slots and then go over the stored _guid codes, to find out which player this index belongs to .. if that player is currently set to the wrong index then update it .. and for the player that lost connection you could set his index to noone or something and have your other code skip control check for him (also to display some connection loss icon) .. the same code should be possible to use for reconnections
 

Gamebot

Member
Controllers are something I understand fairly well... First off you need to understand that each game pad has an index somewhere between 0 and 12. You will need this so that the correct button on the correct controller is doing what you want. When you check a game pad button pressed the syntax looks like this:

gamepad_button_check_pressed ( index, button )


You will need to understand that:
X-input (x-box and switch) Start at 0 go to 3
D-input (Playstation) start at 4 - 11

Luckily for you Game Maker Studio does this for you. You just need to know how to use the System Asynch. Here is an example of all twelve slots, where the yellow ones are the plugged in game pads. The index of each is at the very top when...

Index 0 is Switch, 1 is X-Box, finally 4 is my PS4. ( Dont mind my PS4 looking goofey bad wire. But I am pressing the L2 btn (bottom left shoulder))
GP screen.png


Unlike the example above you will want to save ONLY the indexes plugged in. In a controller or gp object I usually create a global ds_list ( 1d-array ). Using a list allows you to directly add, delete and check for a specific index using the built in functions.

Create:
GML:
global.gplist = ds_list_create();
Cleanup:
GML:
if (ds_exists(global.gplist, ds_type_list)) {
 ds_list_destroy(global.gplist)
}
else {
 exit;
}
The next step is to get these indexes and save them to our list. We do this in the :
ADD EVENT > ASYNCH > SYSTEM

GML:
switch(async_load[? "event_type"])
{

// If a controller is plugged in
 case "gamepad discovered":

 var pad = async_load[? "pad_index"]
 
 gamepad_set_axis_deadzone(pad, .1)
 ds_list_add(global.gplist, pad)
 break;
 
// Controller is disconnected or loss of signal
 case "gamepad lost":
 var pad = async_load[? "pad_index"]
 
 var pos = ds_list_find_index(global.gplist, pad);
 ds_list_delete(global.gplist, pos);

}
This is the confusing part....so a quick explanation. The "?" is a shortcut called an accessor to another data structure known as a map. A map is similar to a two column 2d array where the left column "0" has a list of lookup values known as keys. When it finds that key it automatically returns what is in the same row but column "1". Here is a visual though not exact it gives you the idea of how maps work.



Map name:​
asynch_map​
0​
1​
"event_type"​
[“gamepad_lost”, “gamepad_discovered” ]​
“pad index”​
index of pad​


This line:

"switch (async_load[? "event_type"])"

Checks if the value is gamepad lost or connected. We then use a switch statement to only run which ever is returned.


GML:
 case "gamepad discovered":

  // Code block

 break;


case "gamepad lost":

  // Code block

 break;
Within each of those code blocks there is the:
var pad = async_load[? "pad_index"]

In which our variable pad is the index then we simply add or delete from the list accordingly.

Note:

Due to tiny manufacturing defects, which are natural, and the fact that game pads can be very sensitive, your analog sticks, known as axises will need this line.

gamepad_set_axis_deadzone(pad, .1)

The "deadzone" is how far you need to push a button, hat, or axis before it starts to trigger. Values generally go from 0-1 Except for the "horizontal axis left" and the "vertical axis up" which are values from -1 to 0.

Now just check your list for all plugged in indexes and program. Here is a snippet of my editable textbox using gamepads to control them.

scr_gps
GML:
// Gamepad Temporary

// Loop through all indexes
for(var i = 0; i < ds_list_size(global.gplist); i++){
 
// Get each index and store in "val"
 var val = ds_list_find_value(global.gplist, i);

// If these buttons are pressed they will return a boolean value of true ( 1 )
 var curleft  = gamepad_button_check_pressed(val, gp_padl);
 var curright = gamepad_button_check_pressed(val, gp_padr);
 var curbac   = gamepad_button_check_pressed(val, gp_face3);
 var curdel   = gamepad_button_check_pressed(val, gp_face2);

// If values = 1 run code blocks. in ouf case linked to other scripts
 if (curleft)   {scr_curleft()}
 if (curright) {scr_curright()}
 if (curbac)  {scr_bac()}
 if (curdel) {scr_del()}
}
If you want a specific controller to a certain person, such as a two player you can create this person using the instance create functions in the "gamepad_discovered" code block then give that person the index of the pad.

You can also create a simple 2d array, in a contorller, in which each "index" within the list also has a value so that you need to click "start" for something to happen.

One last ramble. You may want to create macros so that your game pad functions are easier to read and write. I always do this in any script.

GML:
// Resolution and aspect ratio
#macro diswid display_get_width()
#macro dishei display_get_height()
#macro aspect diswid/dishei

// Gamepad //

// Right buttons (A, B, X, Y)
#macro bd gp_face1
#macro br gp_face2
#macro bl gp_face3
#macro bu gp_face4

// D-pad/HATS (Left arrows)
#macro pd gp_padd
#macro pr gp_padr
#macro pl gp_padl
#macro pu gp_padu

// Select and start
#macro select gp_select
#macro start  gp_start

// Left shoulders
#macro l1 gp_shoulderl
#macro l2 gp_shoulderlb
#macro l3 gp_stickl

// Right shoulders
#macro r1 gp_shoulderr
#macro r2 gp_shoulderrb
#macro r3 gp_stickr

// Left Axis
#macro lh gp_axislh
#macro lv gp_axislv

// Right Axis
#macro rh gp_axisrh
#macro rv gp_axisrv
Hopefully, some of this made sense? Good Luck.
 

kburkhart84

Firehammer Games
Now, there is a chance that one controller could lose connection and they will end up changing index as this happens,
I don't think the indices actually change when that happens...if the game were restarted it would make sense, but I don't think during a single execution that it does. I'll have to make a test and figure it out.
 

TailBit

Member
I don't think the indices actually change when that happens...if the game were restarted it would make sense, but I don't think during a single execution that it does. I'll have to make a test and figure it out.
Someone made a post asking for help with this behavior, so that's why I recommend doing that, but then there is also a problem where some controllers share the same guid *shrugs*
 
Top