Force Feedback
Last Updated: May 25th, 2024
Now that we know how to how to use joysticks with SDL, we can use either the old haptics API or the new game controller API to make our controller rumble.//Game controller handler with force feedback SDL_GameController* gGameController = NULL; //Joystick handler with haptic SDL_Joystick* gJoystick = NULL; SDL_Haptic* gJoyHaptic = NULL;
Ok so this is a bit confusing so bear with me for a bit.
Say you are trying to use an Xbox controller in your application. Back in the SDL 1.x days, you used the joystick API we used in the previous tutorial. When SDL 2.0 rolled around, we got the new SDL_Haptic API. A haptic device is something that gives some sort of physical feedback. In this case, it makes the controller rumble but there is also rumble for devices like mice.
Then in more recent versions of SDL, there is the new game controller API. SDL is a wrapper for native APIs and the API for controllers like the Xbox is different from the old school joystick APIs. For this tutorial we are going to use both APIs to make the controller rumble depending on what is supported.
First we have the SDL_GameController which is an all in one controller/rumble interface. Then we have the SDL joystick and haptic to support older APIs.
Oh and please make sure you're on the latest version of SDL. I don't want to get a bunch of e-mail complaining how this code doesn't work because the game controller API is a relatively recent addition.
Say you are trying to use an Xbox controller in your application. Back in the SDL 1.x days, you used the joystick API we used in the previous tutorial. When SDL 2.0 rolled around, we got the new SDL_Haptic API. A haptic device is something that gives some sort of physical feedback. In this case, it makes the controller rumble but there is also rumble for devices like mice.
Then in more recent versions of SDL, there is the new game controller API. SDL is a wrapper for native APIs and the API for controllers like the Xbox is different from the old school joystick APIs. For this tutorial we are going to use both APIs to make the controller rumble depending on what is supported.
First we have the SDL_GameController which is an all in one controller/rumble interface. Then we have the SDL joystick and haptic to support older APIs.
Oh and please make sure you're on the latest version of SDL. I don't want to get a bunch of e-mail complaining how this code doesn't work because the game controller API is a relatively recent addition.
//Initialize SDL if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER ) < 0 ) { printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() ); success = false; }
Like with the joysticks subsystem, you need to make sure to initialize the haptic and game controller specific subsystems in order to use them.
//Check for joysticks if( SDL_NumJoysticks() < 1 ) { printf( "Warning: No joysticks connected!\n" ); } else { //Check if first joystick is game controller interface compatible if( !SDL_IsGameController( 0 ) ) { printf( "Warning: Joystick is not game controller interface compatible! SDL Error: %s\n", SDL_GetError() ); } else { //Open game controller and check if it supports rumble gGameController = SDL_GameControllerOpen( 0 ); if( !SDL_GameControllerHasRumble( gGameController ) ) { printf( "Warning: Game controller does not have rumble! SDL Error: %s\n", SDL_GetError() ); } }
First we're going to attempt to open the joystick as a game controller by checking if it is compatible with the game controller interface with SDL_IsGameController. If it is, we open it with SDL_GameControllerOpen and
check if it has rumble with SDL_GameControllerHasRumble.
And no, I did not forget to link to the documentation for the SDL controller API calls. The API is so new, it is not fully documented on the SDL website as of this writing. Fortunately, if you look in the headers, things are documented. Get used to it because in the industry, documentation is often a luxury and learning to figure out what undocumented code does is an important skill.
And no, I did not forget to link to the documentation for the SDL controller API calls. The API is so new, it is not fully documented on the SDL website as of this writing. Fortunately, if you look in the headers, things are documented. Get used to it because in the industry, documentation is often a luxury and learning to figure out what undocumented code does is an important skill.
//Load joystick if game controller could not be loaded if( gGameController == NULL ) { //Open first joystick gJoystick = SDL_JoystickOpen( 0 ); if( gJoystick == NULL ) { printf( "Warning: Unable to open joystick! SDL Error: %s\n", SDL_GetError() ); } else { //Check if joystick supports haptic if( !SDL_JoystickIsHaptic( gJoystick ) ) { printf( "Warning: Controller does not support haptics! SDL Error: %s\n", SDL_GetError() ); } else { //Get joystick haptic device gJoyHaptic = SDL_HapticOpenFromJoystick( gJoystick ); if( gJoyHaptic == NULL ) { printf( "Warning: Unable to get joystick haptics! SDL Error: %s\n", SDL_GetError() ); } else { //Initialize rumble if( SDL_HapticRumbleInit( gJoyHaptic ) < 0 ) { printf( "Warning: Unable to initialize haptic rumble! SDL Error: %s\n", SDL_GetError() ); } } } } }
If the joystick is not game controller interface compatible, we load it as a plain old joystick.
After we open the joystick, we need to get the haptics device from the joystick using SDL_HapticOpenFromJoystick on an opened joystick. If we manage to get the haptic device from controller we have to initialize the rumble using SDL_HapticRumbleInit.
After we open the joystick, we need to get the haptics device from the joystick using SDL_HapticOpenFromJoystick on an opened joystick. If we manage to get the haptic device from controller we have to initialize the rumble using SDL_HapticRumbleInit.
void close() { //Free loaded images gSplashTexture.free(); //Close game controller or joystick with haptics if( gGameController != NULL ) { SDL_GameControllerClose( gGameController ); } if( gJoyHaptic != NULL ) { SDL_HapticClose( gJoyHaptic ); } if( gJoystick != NULL ) { SDL_JoystickClose( gJoystick ); } gGameController = NULL; gJoystick = NULL; gJoyHaptic = NULL; //Destroy window SDL_DestroyRenderer( gRenderer ); SDL_DestroyWindow( gWindow ); gWindow = NULL; gRenderer = NULL; //Quit SDL subsystems IMG_Quit(); SDL_Quit(); }
Once we're done with a game controller or joystick/haptic device, we should call the corresponding functions to close them.
//Joystick button press else if( e.type == SDL_JOYBUTTONDOWN ) { //Use game controller if( gGameController != NULL ) { //Play rumble at 75% strength for 500 milliseconds if( SDL_GameControllerRumble( gGameController, 0xFFFF * 3 / 4, 0xFFFF * 3 / 4, 500 ) != 0 ) { printf( "Warning: Unable to play game contoller rumble! %s\n", SDL_GetError() ); } } //Use haptics else if( gJoyHaptic != NULL ) { //Play rumble at 75% strength for 500 milliseconds if( SDL_HapticRumblePlay( gJoyHaptic, 0.75, 500 ) != 0 ) { printf( "Warning: Unable to play haptic rumble! %s\n", SDL_GetError() ); } } }
For this application, we'll make the controller shake when a button is pressed.
First we check if the game controller interface was loaded. If it is, we make the controller rumble at 75% strength for 500 milliseconds. Strength goes from 0 to 0xFFFF hex. Again, this is all documented in the headers.
If the haptic was loaded instead, we call SDL_HapticRumblePlay, which takes in the haptic device, strength in percentage, and duration of the rumble. Here we make the controller rumble at 75% strength for half a second whenever a SDL_JoyButtonEvent happens.
Now the SDL 2 game controller and haptics API has many more features not covered here including making custom effects, handling multi rumble devices, and handling haptic mice. You can check them out in the SDL 2 force feedback documentation or dig into the header files for the game controller documentation.
First we check if the game controller interface was loaded. If it is, we make the controller rumble at 75% strength for 500 milliseconds. Strength goes from 0 to 0xFFFF hex. Again, this is all documented in the headers.
If the haptic was loaded instead, we call SDL_HapticRumblePlay, which takes in the haptic device, strength in percentage, and duration of the rumble. Here we make the controller rumble at 75% strength for half a second whenever a SDL_JoyButtonEvent happens.
Now the SDL 2 game controller and haptics API has many more features not covered here including making custom effects, handling multi rumble devices, and handling haptic mice. You can check them out in the SDL 2 force feedback documentation or dig into the header files for the game controller documentation.