GMS 2 How to "Reserve" a Controller Slot for Player 1?

I have my game setup where if there are more than 1 player, they can each use a controller. But, how would I "reserve" a controller slot for player 1? Since the way I currently have it working is, that in 1 player mode, the 1st player can use either the keyboard OR a controller. But, because of the way I have it working, in order for the 2nd player to be able to play, there needs to be at least 2 controllers connected so that both Slot 0 and Slot 1 are filled, so the 2nd player can use the controller to control their player. (Even if Player 1 doesn't use their controller).

How could I make it where I only need 1 controller for 2 player mode? But, "reserve" Slot 0 for Player 1 if they later connected a controller? Hopefully I'm explaining this right. Basically, how I "act like" there's a controller in Slot 0, but there's really not, but with only 1 controller connected it connects to Slot 1, not Slot 0, so the 2nd player can use it. But, if player 1 did want to connect a controller for them, it would then connect to the "reserved" controller slot 0.

Here's my Input code for Player 1 and Player 2 and here a reference on how I use it. For instance, if I wanted a player to shoot a bullet:

Both of these lines of code are in the player object's Step Event.

// Fire laser for Player 1

if o_input.action_one_pressed_ {       
        audio_play_sound(a_laser, 1, false);
    // Fire laser for Player 2

if o_inputP2.action_one_pressed_ {       
        audio_play_sound(a_laser, 1, false);
Code for o_input (Player 1's input method)

Create (same for player 2):

Begin Step (same for player 2):

User Event 0:
Xbox_One_Controller_1_ = 0; // Slot 0
    // Movement
    right_ = keyboard_check(vk_right) or gamepad_axis_value(Xbox_One_Controller_1_, gp_axislh) > 0.50 or gamepad_button_value(Xbox_One_Controller_1_, gp_padr);
    left_  = keyboard_check(vk_left)  or gamepad_axis_value(Xbox_One_Controller_1_, gp_axislh) < -0.50 or gamepad_button_value(Xbox_One_Controller_1_, gp_padl);
    up_    = keyboard_check(vk_up)    or gamepad_axis_value(Xbox_One_Controller_1_, gp_axislv) < -0.40 or gamepad_button_value(Xbox_One_Controller_1_, gp_padu);
    down_  = keyboard_check(vk_down)  or gamepad_axis_value(Xbox_One_Controller_1_, gp_axislv) > 0.40 or gamepad_button_value(Xbox_One_Controller_1_, gp_padd);
    right_pressed_ = keyboard_check_pressed(vk_right) or gamepad_button_check_pressed(Xbox_One_Controller_1_, gp_padr);
    left_pressed_  = keyboard_check_pressed(vk_left)  or gamepad_button_check_pressed(Xbox_One_Controller_1_, gp_padl);
    up_pressed_    = keyboard_check_pressed(vk_up)    or gamepad_button_check_pressed(Xbox_One_Controller_1_, gp_padu);
    down_pressed_  = keyboard_check_pressed(vk_down)  or gamepad_button_check_pressed(Xbox_One_Controller_1_, gp_padd);
    // Action Keys

    menu_pause_pressed_ = keyboard_check_pressed(vk_enter) or gamepad_button_check_pressed(Xbox_One_Controller_1_, gp_start);
    pause_pressed_   = keyboard_check_pressed(vk_shift) or gamepad_button_check_pressed(Xbox_One_Controller_1_, gp_select);

    action_one_   = gamepad_button_check(Xbox_One_Controller_1_, gp_face3) or keyboard_check(ord("D"));
    action_two_   = gamepad_button_check(Xbox_One_Controller_1_, gp_face2) or keyboard_check(ord("A"));
    action_three_ = gamepad_button_check(Xbox_One_Controller_1_, gp_face4) or keyboard_check(ord("W"));
    action_four_  = gamepad_button_check(Xbox_One_Controller_1_, gp_face1) or keyboard_check(ord("S"));

    action_one_pressed_   = gamepad_button_check_pressed(Xbox_One_Controller_1_, gp_face3) or keyboard_check_pressed(ord("D"));
    action_two_pressed_   = gamepad_button_check_pressed(Xbox_One_Controller_1_, gp_face2) or keyboard_check_pressed(ord("A"));
    action_three_pressed_ = gamepad_button_check_pressed(Xbox_One_Controller_1_, gp_face4) or keyboard_check_pressed(ord("W"));
    action_four_pressed_  = gamepad_button_check_pressed(Xbox_One_Controller_1_, gp_face1) or keyboard_check_pressed(ord("S"));
    action_one_released_    = gamepad_button_check_released(Xbox_One_Controller_1_, gp_face3) or keyboard_check_released(ord("D"));
    action_two_released_    = gamepad_button_check_released(Xbox_One_Controller_1_, gp_face2) or keyboard_check_released(ord("A"));
    action_three_released_  = gamepad_button_check_released(Xbox_One_Controller_1_, gp_face4) or keyboard_check_released(ord("W"));
    action_four_released_   = gamepad_button_check_released(Xbox_One_Controller_1_, gp_face1) or keyboard_check_released(ord("S"));
Code for o_inputP2 (Player 2's input method) The same as Player 1's, except no keyboard controls.

Xbox_One_Controller_2_ = 1; // Slot 1
    // Movement
    right_ = gamepad_axis_value(Xbox_One_Controller_2_, gp_axislh) > 0.50 or gamepad_button_value(Xbox_One_Controller_2_, gp_padr);
    left_  = gamepad_axis_value(Xbox_One_Controller_2_, gp_axislh) < -0.50 or gamepad_button_value(Xbox_One_Controller_2_, gp_padl);
    up_    = gamepad_axis_value(Xbox_One_Controller_2_, gp_axislv) < -0.40 or gamepad_button_value(Xbox_One_Controller_2_, gp_padu);
    down_  = gamepad_axis_value(Xbox_One_Controller_2_, gp_axislv) > 0.40 or gamepad_button_value(Xbox_One_Controller_2_, gp_padd);
    right_pressed_ = gamepad_button_check_pressed(Xbox_One_Controller_2_, gp_padr);
    left_pressed_  = gamepad_button_check_pressed(Xbox_One_Controller_2_, gp_padl);
    up_pressed_    = gamepad_button_check_pressed(Xbox_One_Controller_2_, gp_padu);
    down_pressed_  = gamepad_button_check_pressed(Xbox_One_Controller_2_, gp_padd);
    // Action Keys

    pause_pressed_ = gamepad_button_check_pressed(Xbox_One_Controller_2_, gp_start);
    menu_pause_pressed_   = gamepad_button_check_pressed(Xbox_One_Controller_2_, gp_select);

    action_one_   = gamepad_button_check(Xbox_One_Controller_2_, gp_face3);
    action_two_   = gamepad_button_check(Xbox_One_Controller_2_, gp_face2);
    action_three_ = gamepad_button_check(Xbox_One_Controller_2_, gp_face4);
    action_four_  = gamepad_button_check(Xbox_One_Controller_2_, gp_face1);

    action_one_pressed_   = gamepad_button_check_pressed(Xbox_One_Controller_2_, gp_face3);
    action_two_pressed_   = gamepad_button_check_pressed(Xbox_One_Controller_2_, gp_face2);
    action_three_pressed_ = gamepad_button_check_pressed(Xbox_One_Controller_2_, gp_face4);
    action_four_pressed_  = gamepad_button_check_pressed(Xbox_One_Controller_2_, gp_face1);
    action_one_released_   = gamepad_button_check_released(Xbox_One_Controller_2_, gp_face3);
    action_two_released_   = gamepad_button_check_released(Xbox_One_Controller_2_, gp_face2);
    action_three_released_ = gamepad_button_check_released(Xbox_One_Controller_2_, gp_face4);
    action_four_released_  = gamepad_button_check_released(Xbox_One_Controller_2_, gp_face1);


Have a gamepad setup script that checks how many controllers are connected. If there's only 1, then make player 2's slot 0. If there's 2, Player 2 controller ID = 1. Run it at the beginning of the game.
Then make sure you have an async-system event to test for controllers being plugged in and out:
//async-system event
var msg = ds_map_find_value(async_load, "event_type");
if(msg == "gamepad lost" || msg == "gamepad discovered"){
    //call the same gamepad setup script here to recount and reset the ID numbers


Firehammer Games
I feel like you could better separate the input from the action. My input system lets players select their own inputs. And each step it only checks the inputs that are actually selected. You should simply store a variable(or a couple as needed) for each action on each player. This variable would tell you what input to check for each action. If you make it more fluid like this, you don't have to worry about which gamepad is on which slot, and which player is using which one, etc...

I don't think you should limit the players in any fashion. Why make it where player 2 can only use slot 1? why player 1 can use only keyboard or slot 0? What happens if there are multiple gamepads connected and the player wants to use slot 3, 4, etc...? If you simply directly let the player choose their own input action mapping, you don't have to worry about it. I've heard of players wanting to do strange things like combine keyboard with gamepad inputs because they like the gamepad axes but not the buttons, or the other way around. Also, you never know what slots the gamepads are going to have. There have been strange instances with Windows keeping slots on certain controllers even when disconnecting others, as in controllers could keep higher slot numbers even after disconnected the ones that have the lower slots. I also see that you are not using the higher slots(4 and up), so you are not supporting any DirectInput devices, rather only XInput stuff. I don't think that's a good thing by any stretch. I think you should support basically any and every input the player would have, in whatever combinations they would choose.


In my own game, I have separate values for players, "slots", and controllers.

Whenever a player presses a button in the menu, that controller is assigned to the lowest numbered player that is not already active (as-in first one is Player 1), and set to the first unused slot.

I got a variable called "player_in_slot" and another one called "player_controller".

"player_in_slot" is an array used to assign and check which player is on which slot. For example, "player_in_slot[2] = 3" means that Player 3 is currently on slot 2. (Separating players from slots is important for my game, but might be irrelevant for yours, so you might not necessarily need this)

To go along with that, "player_controller" (also an array) is used to check which player is using which controller. "player_controller[3] = 0" means that Player 3 is using the first gamepad.

To properly use the keyboard on any player, I have a... virtual gamepad of sorts that's used for keyboard inputs. In the "player_controller[]" variable, all gamepads go from 0-9, since that's what Game Maker supports and uses. Controllers 10-13, though, are all used for keyboard functions. If you press any key on the menu and there's already a Player 1 using controller 0 in slot 1, then "player_in_slot[2]" will be set to 2, and "player_controller[2]" will be set to 10, meaning that Player 2 is now using controller 10, which is a keyboard.

For inputs, I'm using three separate scripts. They're called input_check(), input_check_pressed() and input_check_released(). The scripts take arguments for the player number, and for the specific action to be checked, as a string (you might want to use enums instead. I don't because I'm a dumb dumb).

Inside the script, the thing basically checks if the value of the current player's controller is larger or smaller than 10. If it's less than 10, the controller is a gamepad and gamepad functions are used. If it's 10 or more, the controller is a keyboard, and keyboard functions are used. Both keyboards and gamepads have fully separate code inside the script, but they both use the name of the action to check which specific button/key for that action is saved to that controller.

So like, if player 3 wants to jump, you write "input_check_pressed(3,"jump)". Then inside the script, "player_controller[argument0]" is equal to 0, which is a gamepad. So inside the gamepad code, it switches argument1, goes to the case for "jump", and it returns gamepad_button_check(player_controller[argument0],(jumping_button))

I have no idea if this is necessarily a good way to do things. It can get a bit messy if you want to even slightly rework the logic (I had to when I wanted to add CPU players into the system and it was not nice), or move players from one slot to another or **** like that. But ever since I got it working, it hasn't failed me and it's super automated and works under all circumstances. It's super easy to let every player customize their buttons, and it allows you to do pretty much everything, since you can use any controller on any player on any slot and change all of them at will. There's likely ways to simplify it, but I hope it helps! Something to also consider is that unlike the examples I wrote here, player numbers and slot numbers don't actually match what is shown to the player, since arrays start from 0. What is internally known as player[1], in-game is displayed as Player 2, because Player 1 is actually player[0].
Last edited: