• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

How to Draw Pressed Controller Button to GUI?

I've been looking for a way to draw the number of a pressed button on a controller to the GUI and I found this this bit of code, and have it in a Draw GUI Event, but it doesn't draw anything on the GUI. I'm using an Xbox One Controller to test it out.

GML:
for (var i = gp_face1; i < gp_axisrv; i++) {
    if gamepad_button_check(0, i) return i;
}
return false;

draw_text(500, 500, string(i));
Am I just entering the wrong variable for it to draw?
Basically, what I'm wanting to do is, when the gp_face1 button is pressed (or any other button), draw what the number for it is on the GUI.
 
Last edited:
What does i show when you run this?
When I first run the game, it doesn't show any text where it's supposed to draw. Not even when I press a button. I know the controller is giving input, because the player object is moving and reacting to the button presses.
 

Amon

Member
Maybe your way of getting the gamepad buttons is wrong. Have you tried iterating with gamepad_axis_value()?
 
Maybe your way of getting the gamepad buttons is wrong. Have you tried iterating with gamepad_axis_value()?
Just tried that. Still not drawing anything.

I've seen some people use numbers instead of gp_face1 in the var i = gp_face1; i < gp_axisrv; i++ But I don't know the maximum number the buttons go to.
 
Nothing happens after a return. Because execution has returned to the calling scope. No text is being drawn because there will never be a call to draw_text.
Where should I draw it then? If I draw it before the return, it still doesn't draw and if I draw it in the for loop, it shows text, but it draws a ton of text and can't tell what it is.
 

Amon

Member
Remove the return and test it. Exit the for loop with a proper break command when a button has been pressed if needed.
 

kburkhart84

Firehammer Games
I've been looking for a way to draw the number of a pressed button on a controller to the GUI and I found this this bit of code, and have it in a Draw GUI Event, but it doesn't draw anything on the GUI. I'm using an Xbox One Controller to test it out.

GML:
for (var i = gp_face1; i < gp_axisrv; i++) {
    if gamepad_button_check(0, i) return i;
}
return false;

draw_text(500, 500, string(i));
Am I just entering the wrong variable for it to draw?
Basically, what I'm wanting to do is, when the gp_face1 button is pressed (or any other button), draw what the number for it is on the GUI.
Is all that code in the same place? If it is, that return call that returns false is going to happen no matter what, and it never gets to the draw call. What you would need to do is just break out of the for() statement and not return false just after, and THEN draw the i variable.

Furthermore...are you sure that gp_face1 and gp_axisrv are nice and sequential, the first lower than the second, with all the other buttons in between? I've never tested that so I couldn't confirm. I generally assume that constants that you don't make yourself shouold not be used in this manner, as it is similar to using the numbers that object and instance ids are.
 
Remove the return and test it. Exit the for loop with a proper break command when a button has been pressed if needed.
Is this what you mean?

GML:
for (var i = gp_face1; i < gp_axisrv; i++) {
    if gamepad_button_check(0, i) return i;
    draw_text(500, 500, string(i));
    break;
}
I removed the return = false; But when the game is run, the text shows 32769 and when I press a face button it just blinks.
 
Last edited:

kburkhart84

Firehammer Games
Is this what you mean?

GML:
for (var a = gp_face1; a < gp_axisrv; a++) {
    if gamepad_button_check(0, a) return a;
    draw_text(500, 500, string(a));
    break;
}
I removed the return = false; But when the game is run, the text shows 32769 and when I press a face button it just blinks.
Your problem here is that if a button is being pressed, you are STILL returning, instead of continuing and drawing the text. Finally, the text just draws whatever happens to be in the variable a at the time, even if the button isn't actually being pressed.

You basically have simple code logic problems.
 
Your problem here is that if a button is being pressed, you are STILL returning, instead of continuing and drawing the text. Finally, the text just draws whatever happens to be in the variable a at the time, even if the button isn't actually being pressed.

You basically have simple code logic problems.
Are you saying I need to get rid of the return then?

I've tried multiple variations and still it either doesn't draw or just draws a 0 or 32769.
 

kburkhart84

Firehammer Games
Are you saying I need to get rid of the return then?

I've tried multiple variations and still it either doesn't draw or just draws a 0 or 32769.
Basically yes, the return will always exit the code right then, so it is not getting to the draw code. You can put a break statement instead which would exit the for() loop but still continue to the code that is outside of the for loop.

Code:
for (var i = gp_face1; i < gp_axisrv; i++)
{
    if gamepad_button_check(0, i)
    {
        draw_text(500, 500, string(i));
        break;
    }
}
See the logic of that code. As soon as you get a positive button confirmation, it will draw the text for that button, and then it uses break to exit the for loop. Note that it will also only pick up the first button it finds, so if you are pressing more than one you won't see it here.

I mentioned earlier about using the gp_constants like this. If gp_face1 is a lower number than gp_axisrv, and you are sure that all the buttons are within those two numbers, then you are fine. But I never count on the actual numerical values of constants that I didn't myself make, so be aware that you may or may not have an issue here.
 
Basically yes, the return will always exit the code right then, so it is not getting to the draw code. You can put a break statement instead which would exit the for() loop but still continue to the code that is outside of the for loop.

Code:
for (var i = gp_face1; i < gp_axisrv; i++)
{
    if gamepad_button_check(0, i)
    {
        draw_text(500, 500, string(i));
        break;
    }
}
See the logic of that code. As soon as you get a positive button confirmation, it will draw the text for that button, and then it uses break to exit the for loop. Note that it will also only pick up the first button it finds, so if you are pressing more than one you won't see it here.

I mentioned earlier about using the gp_constants like this. If gp_face1 is a lower number than gp_axisrv, and you are sure that all the buttons are within those two numbers, then you are fine. But I never count on the actual numerical values of constants that I didn't myself make, so be aware that you may or may not have an issue here.
For the most part, I think it's working. I changed it a little to work in my game instead of an example project.
Instead of the constants, I added 0 and 32769. But now, it draws 4100 at the start of the game and when an input is pressed it shows the pressed input number, but then goes back to 4100 it showed at start of the game. 4100 appears to be a joystick value, but I'm not checking for the joystick value. I don't think it's a big deal, but why is it showing it?

This is what I changed it to:

GML:
for (var i = 0; i < 32769; i++)
{
    if gamepad_button_check(global.Controller_Slot[0], i)
    {
        draw_text(100, 100, string(i));
        break;
    }
}
 

gnysek

Member
Hint:
return is only for functions. In events, it works same as exit.

My another hint:
GML:
var btns = [gp_face1, gp_face2, gp_face3, gp_face4]; // add more here
var nams = ["A | X","B | O","X | []","Y | /\"]; //also add more, first is xbox, second psx
var y = 0;

for(var i = 0; i < array_length(btns); i++) {
    if (gamepad_button_check(global.Controller_Slot[0], btns[i])) {
        draw_text(100, 100 + y, nams[i]);
        y+=20;
    }
}
You need to put into arrays all constants from this page (maybe except axis, but including stick): https://manual.yoyogames.com/#t=GameMaker_Language/GML_Reference/Game_Input/GamePad_Input/Gamepad_Input.htm
 

kburkhart84

Firehammer Games
For the most part, I think it's working. I changed it a little to work in my game instead of an example project.
Instead of the constants, I added 0 and 32769. But now, it draws 4100 at the start of the game and when an input is pressed it shows the pressed input number, but then goes back to 4100 it showed at start of the game. 4100 appears to be a joystick value, but I'm not checking for the joystick value. I don't think it's a big deal, but why is it showing it?
I couldn't tell you on that one. I'm sure the function is only designed to properly work with values between 0 and 31(for direct checking instead of mapped values, which is mainly for DInput gamepads that support up to 32 buttons but don't have defined gp_constants for them all) and whatever the gp_constants are, and I don't know what 4100 would be. This is certainly part of why I mentioned that you should not be looping constants like this in the first place, rather you should be directly checking the values. That what gnysek's post is referring too, as it adds the things to an array and then loops through that instead. This way, no matter what happens to the constants, say for some reason Yoyo decides to change them in the future, your code won't break, because you are using the constants only and not depending on the actual numerical value for anything.
 
The code I'm using now doesn't check the constants. It loops though numbers and basically works. It just shows a random number depending on the connected controller.

I'll do more testing to see how it works.
 

gnysek

Member
It's hard to position analog sticks directly to 0,0, so it's possible that they are always treated as pressed. Also, as 4100 is probably over gm constants, who knows what you're really checking - as we don't know how internally that function works. You shouldn't check anything that is not mentioned in manual IMO.
 

kburkhart84

Firehammer Games
The code I'm using now doesn't check the constants. It loops though numbers and basically works. It just shows a random number depending on the connected controller.

I'll do more testing to see how it works.
I know...I just mention that using random numbers is also similar to just looping through from one constant to another in that you don't know the correct values. That's why I mentioned what the function was designed to actually work with(specifically either the constants, or values from 0 - 31). I have any clue at all why 4100 would ever return true if all those other values don't, but in normal usage it never comes up since 4100 isn't one of the correct values to be used in the first place.

*EDIT

It's hard to position analog sticks directly to 0,0, so it's possible that they are always treated as pressed. Also, as 4100 is probably over gm constants, who knows what you're really checking - as we don't know how internally that function works. You shouldn't check anything that is not mentioned in manual IMO.
That's why there are multiple functions for these things. You wouldn't theoretically check axis values using "button" functions. My system has separate stuff to handle all of those things, including needing a POV function since DInput devices have those sometimes.

As far as the gp_constants, I'm pretty sure they are all something like 32xxx from what I've seen...but I've mentioned multiple times that you never count on the numerical value of constants. The only reason I know to use values 0 - 31 instead of gp_constants(since that isn't in the manual) is that somebody pointed it out to me at one point, I can't remember if it was Russell or Nocturne. I had made a post or sent in a ticket or something years ago since I knew DInput devices support 32 buttons and there are certainly not that many gp_constants for buttons. It is something that should probably be added to the manual but I think they don't care much about those since so much stuff revolves around XInput these days.
 

gnysek

Member
Still, those are GM internal values, not DInput or XInput - they are translated in-engine to proper values.
 
Thank you everyone for helping out with the code. But since the 4100 number doesn't appear to have any meaning right off, I'm going to say that the for loop code is work for the purpose.

I'm want to eventually create a custom controller mapping system for players that's in an easy to understand system. I wanted controller inputs drawn to the GUI to see what the number was when an input was pressed. Since it's working for the most part, I'll test it out more later as custom mapping.
 

kburkhart84

Firehammer Games
Still, those are GM internal values, not DInput or XInput - they are translated in-engine to proper values.
Certainly, all the gp_constants are translated. But if you use 0 - 31 for buttons, and 0 - 9 for axes(since DInput devices use max 10 axes and 32 buttons), those values are likely sent directly to the input API as is. Then SDL has the mapping thing where many DInput gamepads are given constants via the mapping systems so they can also use the idea of "face buttons" that match up closely to their XInput counterparts. If you use the gp_constants with devices 4 - 11(on Windows), the input system will automatically use that mapping stuff SDL provides. I personally skip that and just use values 0 - x directly for those devices, mainly because there isn't mapping for ALL available devices, and there aren't gp_constants for all the available buttons. This does mean I have to handle those devices differently from how I handle XInput ones, such that my system has separate "devices" for them. So my system has "not set", then has keyboard, mouse, XInput gamepad, and DInput gamepad, and each device type is handled differently internally.

Thank you everyone for helping out with the code. But since the 4100 number doesn't appear to have any meaning right off, I'm going to say that the for loop code is work for the purpose.
Hey, if it works for you currently and you are aware of the possible pitfalls if you were to keep such code in actual production usage, more power to you. It's important to get something that actually works, and more so than doing it the way somebody else thinks is best.

I'm want to eventually create a custom controller mapping system for players that's in an easy to understand system. I wanted controller inputs drawn to the GUI to see what the number was when an input was pressed. Since it's working for the most part, I'll test it out more later as custom mapping.
You already know Firehammer Input does all that mapping stuff. Feel free to use it to figure out more about how I went about doing it. And you can join the Discord I have or PM me, or post in the FHInput topic if you want help figuring things out as well. I can understand not wanting to directly use it so I fully condone making your own just using it as a reference point.
 

gnysek

Member
I'm want to eventually create a custom controller mapping system
If you want to only cover buttons that are generally on every gamepad,, then iterating over an array filled with 16 gms constants should be enough (cause you can graphically represent those buttons thanks to that). I'm not sure if any additional buttons on custom gamepads will work this way.
 
While testing out the for loop code here in 2 different projects, they seem to give different results with the same controller. The controller I'm using is the PS5 DualSense.

For Loop in Draw GUI

GML:
for (var i = 0; i < 32769; i++)
{
    if gamepad_button_check(global.Controller_Slot[0], i)
    {
        draw_text(320, 180, string(i));
        break;
    }
}
What my project is showing is:

X – 0
O – 1
Square - 2
Triangle – 3

YoYo Games Test Project

X – 1
O – 2
Square - 0
Triangle - 3

The odd thing is that I tested out performing an action based on what each project said was the number for the X button. My project said 0, while YoYo Games Test Project said 1. Both projects recognized the X button's actions even though each project said the X Button was a different number.

For instance, I have in my project that it fires bullets when pressing the X button.

if gamepad_button_check(global.Controller_Slot[0], 0) {
event_user(14);
}

I have in the test project that it fires bullets when pressing the X button.

if gamepad_button_check(pad_num, 1) {
event_user(14);
}

Basically, what I'm asking is why do 2 different projects show that the same controller and the same button have different numbers for it's input action? Shouldn't the number for the input always be the same? Like, shouldn't the X Button on a PS5 DualSense always be 0, id one project says it is?

Would this mean that if in testing, a controller that works in one project, might not work in another?
 

kburkhart84

Firehammer Games
I specifically remember that there was a thing about swapping those two buttons, maybe because the 'X' button is in a different place on XBOX controllers compared to the Switch and PS3/4 controllers. I don't remember details but I DO remember that there was something. I'm going to investigate and I'll let you know.
 

kburkhart84

Firehammer Games
OK, I swear I remember seeing somewhere about being able to swap the two buttons, but I seriously can't find anything about it now.

Basically, what I'm asking is why do 2 different projects show that the same controller and the same button have different numbers for it's input action? Shouldn't the number for the input always be the same? Like, shouldn't the X Button on a PS5 DualSense always be 0, id one project says it is?
I have no idea why the same code and hardware(and same GMS2 version) would produce different results. Have you tried to see if you get different results using the actual gp_constants instead of the raw numbers?

Would this mean that if in testing, a controller that works in one project, might not work in another?
As I've mentioned before, if you have actual mapping in your own code(like my system handles), it won't matter AT ALL. The best way to get the best player experience is to let them directly press the buttons they want to use for the actions. So you tell them to press the button they want to use for "shoot" and no matter what that button is, whether it is square, X, some axis, whatever, you just put that input into the system, and from then on, you set the status of the "shoot" action based on that. This way, even if they have some strange controller that doesn't look anything like XBOX gamepads, things will still work just fine as long as it has drivers and works. You may not even be able to show proper descriptions for it(my system just has generic things like "button 1" in these cases). But as long as the input works for the player, they will not really care. I really don't know of any better way to handle inputs. This is the only way I know that can satisfy every player's needs. This method even works for people who have keyboards from other regions, where WASD keys aren't in the same place. Since they just hit the keys they want, it doesn't matter where they are.
 
Actually, what was happening was, is that I have a setup that based on the controller connected, I assign a pre programed mapping using the gamepad_test_mapping()

GML:
else if gp_description == "Wireless Controller" && gp_GUID == "4c05e60c000000000000504944564944" {

// PlayStation 5 DualSense Controller // GUID 4c05e60c000000000000504944564944
gp_GUID = gamepad_get_guid(global.Controller_Slot[i]);
gp_description = gamepad_get_description(global.Controller_Slot[i]);
var mapping = "a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,";
                      
gamepad_test_mapping(global.Controller_Slot[i], gp_GUID + "," + gp_description + "," + mapping);
That's why in my game the same button was different, but still did the same action. My game had the custom mapping where the YoYo Games test project didn't have the custom setup. I just overlooked something I had already did.

I get the custom mapping from the SDL2 Gamepad Tool by General Arcade.
 
Last edited:

kburkhart84

Firehammer Games
That's why in my game the same button was different, but still did the same action. My game had the custom mapping where the YoYo Games test project didn't have the custom setup. I just overlooked something I had already did.
I'm glad you figured it out. I didn't want to assume you had something different with your code but it was the first thing that crossed my mind. I still think my way is better, just handling the action mapping myself without counting on basically anything else. But if this way works for you, that's legit.

I get the custom mapping from the SDL Gamepad Tool by General Arcade.
I ran across that when I was investigating how SDL's whole mapping system works. I didn't end up needing anything like it though since I just do my own mapping stuff instead. Maybe I'm just old school though...I've been messing with custom input mapping stuff since I was using C++ with vanilla OpenGL(no game engine) and DirectInput for receiving inputs back before XInput was even a thing.
 
Without the custom mapping using the gamepad_test_mapping(), the D-Pad is not showing input when used with the For Loop drawing the input number to the screen. How would I see it's actually number value if it's not being picked up?

So in both projects now, the X button the PS5 DualSense it shows it's 0.

The reason I don't want to use your input system, is because I already have an input system created and working. I just would like it to be more customizable and I really don't understand how yours works. When I tried it with your Input System, it shows the X button on the PS5 DualSense is 2. So, how would I figure out what the actually input number is? Based on the 2 projects I tested, it seems like it's 0.
 

kburkhart84

Firehammer Games
The reason I don't want to use your input system, is because I already have an input system created and working. I just would like it to be more customizable and I really don't understand how yours works. When I tried it with your Input System, it shows the X button on the PS5 DualSense is 2. So, how would I figure out what the actually input number is? Based on the 2 projects I tested, it seems like it's 0.
Don't get me wrong, I understand why you don't use my system, my main point about my system was that you could use it to figure out how to add yours the features you don't have yet(AFAIK anyway). My thoughts are more that you could open up my example project stuff and take a good look at the code, and see the way I did things and adapt them to your own system. And I did mention that I'm willing to actually help you with analyzing my system so you can more easily figure out how it works.

When I tried it with your Input System, it shows the X button on the PS5 DualSense is 2. So, how would I figure out what the actually input number is? Based on the 2 projects I tested, it seems like it's 0.
My input system doesn't use the GML "mapping" system at all. If the gamepad is an XInput device(#0 - 3), it just uses the gp_constants, which should match up perfectly unless there is a discrepancy between PS5 and XBOX buttons. If the gamepad is not an XInput device(# 4 - 11), I literally just use buttons 0 - 31, and I don't use the constants at all. This lets me not be subject at all to the mapping system, so it is going to be the button number as reported by the gamepad driver without being filtered through the SDL database mapping stuff. Therefore, it may not be the same thing you expect if you are using the gp_constants with the DInput gamepad since those get filtered by the mapping system. So if it is a DInput device and the button is reading as description 'Button 2' in my system, then that's the "real" button index, except that you have to consider that the indices technically start at 0 while my description strings start at 1.
 
For now, my input system supports most of the main console controllers as far as testing goes. I think that's a good starting position so I don't spend a lot of time working on a customizable input system.

As long as it works with Xbox One, Xbox Series S|X, Nintendo Switch Pro Controller and PS4 DualShock 4 and PS5 DualSense I'm happy. I've personally tested these controllers and have programmed custom mapping into the input object if they are used. So I think this is a good starting place for the input system. As I go, I'll learn more about inputs.
 

kburkhart84

Firehammer Games
For now, my input system supports most of the main console controllers as far as testing goes. I think that's a good starting position so I don't spend a lot of time working on a customizable input system.

As long as it works with Xbox One, Xbox Series S|X, Nintendo Switch Pro Controller and PS4 DualShock 4 and PS5 DualSense I'm happy. I've personally tested these controllers and have programmed custom mapping into the input object if they are used. So I think this is a good starting place for the input system. As I go, I'll learn more about inputs.
Indeed, our priorities differ. You want all the modern stuff to work and work well, and without having to have the user map inputs. I want the user to be able to use ANY gamepad that works and have the best in customization options, whether keyboard, mouse, or any gamepads. For many gamers, your way will certainly have an advantage, especially ones that are used to console games where there is not much option for customizing inputs.
 
Top