In this tutorial the goal is to be able to use a gamepad or a joystickw with SDL3, for that I will use a xbox controller.
The first thing to check is wether a gamepad is connected for that you can use the function: SDL_HasGamepad
if (!SDL_HasGamepad()) {
std::cout << "No gamepads connected.\n";
SDL_Quit();
return 0;
}
After we know at least one gamepad is connected we can list the gamepad and pick the first one, the function to list th gamepads is: SDL_GetGamepads
int count = 0;
SDL_JoystickID *ids = SDL_GetGamepads(&count);
SDL_Gamepad* gamepad = NULL;
// Iterate over the list of gamepads
for(int i = 0; i < count; i++) {
SDL_Gamepad* gamepd = SDL_OpenGamepad(ids[i]);
if(gamepad == NULL) {
gamepad = gamepd;
}
std::cout << "Gamepad connected: " << SDL_GetGamepadName(gamepd) << "\n";
// Close the other gamepads
if(i > 0) {
SDL_CloseGamepad(gamepd);
}
}
if (!gamepad) {
std::cerr << "Failed to open gamepad: " << SDL_GetError() << "\n";
SDL_Quit();
return 1;
}
After the gamepad is opened we can record the event when a button is pressed (SDL_EVENT_GAMEPAD_BUTTON_DOWN) and released (SDL_EVENT_GAMEPAD_BUTTON_UP) or when a joysticks is moved (SDL_EVENT_GAMEPAD_AXIS_MOTION):
bool running = true;
SDL_Event event;
while (running) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_EVENT_QUIT:
running = false;
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
std::cout << "Button pressed: "
<< SDL_GetGamepadStringForButton((SDL_GamepadButton)event.gbutton.button)
<< "\n";
break;
case SDL_EVENT_GAMEPAD_BUTTON_UP:
std::cout << "Button released: "
<< SDL_GetGamepadStringForButton((SDL_GamepadButton)event.gbutton.button)
<< "\n";
break;
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
std::cout << "Axis moved: "
<< SDL_GetGamepadStringForAxis((SDL_GamepadAxis)event.gaxis.axis)
<< " Value: " << event.gaxis.value << "\n";
break;
}
}
SDL_Delay(16); // Sleep 16ms (~60FPS)
}
If you test this code you can see the axis of the joystick are trigger very easily, to avoid that you need to define a threshold for a deadzone, this threshold is a constant:
Thumbstick axis values range from SDL_JOYSTICK_AXIS_MIN (-32768) to SDL_JOYSTICK_AXIS_MAX (32768), and are centered within ~8000 of zero, though advanced UI will allow users to set or autodetect the dead zone, which varies between gamepads.
bool running = true;
SDL_Event event;
const int DEADZONE = 9000;
while (running) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_EVENT_QUIT:
running = false;
break;
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
if(event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX && event.gaxis.value > DEADZONE) {
std::cout << "left axis right\n";
} else if(event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX && event.gaxis.value < -DEADZONE) {
std::cout << "left axis left\n";
}
if(event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFTY && event.gaxis.value > DEADZONE) {
std::cout << "left axis down\n";
} else if(event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFTY && event.gaxis.value < -DEADZONE) {
std::cout << "left axis up\n";
}
break;
}
}
SDL_Delay(16); // Sleep ~16ms (~60FPS)
}
The buttons (event.gbutton.button) for the gamepad can have these values:
The values for the axis (event.gaxis.axis) are the following:
Need another OS ? => Windows, Mac, Linux