Skip to content

Instantly share code, notes, and snippets.

@xeekworx
Created June 20, 2022 05:41
Show Gist options
  • Save xeekworx/4ed45c039ea1676ddef1c2d9f921973d to your computer and use it in GitHub Desktop.
Save xeekworx/4ed45c039ea1676ddef1c2d9f921973d to your computer and use it in GitHub Desktop.
SDL Renderer & Keyboard State Example in C
#include <SDL.h>
#include <stdio.h>
// Macros used to keep the box within the screen's boundaries. In C++ std::min and std::max can be used.
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
// This is the forward declaration for shutdown so that main() knows it exists, check below main() for the function
// body.
int shutdown(SDL_Window* main_window, SDL_Renderer* renderer, const char* error_prefix, const char* error);
int main(int argc, char* argv[])
{
const char* app_title = "SDL Box Movement Example - Use the arrow keys!";
const size_t max_app_title_ext = (size_t)strlen(app_title) + 128;
char* app_title_ext = (char*)malloc(max_app_title_ext); // needed when appending FPS to the title
int screen_width = 1024, screen_height = 768;
SDL_Window* main_window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Event event = { 0 };
SDL_bool should_quit = SDL_FALSE;
SDL_FRect box_destination = { 0 }; // Use SDL_FRect instead of SDL_Rect for subpixel rendering.
float update_fps_every_seconds = 1.F; // Every (this many) seconds, update the framerate in the title bar.
float seconds_since_last_fps_update = 0.F; // Keep up with how long it's been for framerate display.
float current_fps = 0.F; // Always set to the current calculated framerate.
// INITIALIZATION:
// ------------------------------------------------------------------------------------------------------------
// Initialize SDL and create a window and renderer
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
free(app_title_ext);
return shutdown(main_window, renderer, "Failed to initialize SDL", SDL_GetError());
}
if ((main_window = SDL_CreateWindow(
app_title,
SDL_WINDOWPOS_CENTERED, // Use Centered Top Coordinate
SDL_WINDOWPOS_CENTERED, // Use Centered Left Coordinate
screen_width, screen_height, // Width & Height
0 // Flags (Window is implicitly shown if SDL_WINDOW_HIDDEN is not set)
)) == 0) {
free(app_title_ext);
return shutdown(main_window, renderer, "Failed to create a window", SDL_GetError());
}
// -1 to use the first available render driver.
// 0 to default to SDL_RENDERER_ACCELERATED (hardware), and fallback or fallback to software if necessary.
if ((renderer = SDL_CreateRenderer(
main_window, // The window to associate with this renderer, multiple windows & renderers are possible
-1, // Index of render driver, -1 for default
0 // Flags, 0 to default to SDL_RENDERER_ACCELERATED (hardware), and fallback or fallback to
// software if necessary.
)) == 0) {
free(app_title_ext);
return shutdown(main_window, renderer, "Failed to create the renderer", SDL_GetError());
}
// Comment this line out if you do not want VSync. VSync is when the rendering waits for the monitor to finish
// drawing the last frame so that you do not start drawing the current frame before it's done. Without VSync
// you could see just how fast your computer can render, but also it prevents seeing screen tearing (only
// partial frames being drawn).
SDL_RenderSetVSync(renderer, 1); // 1 for on, 0 for off.
// With this on you might expect a framerate of around 59 or 60 depending on your monitor. I have a gaming
// monitor with a refresh rate of 165, so I will see my fps as close to 165.
// CONFIGURE THE BOX / PLAYER, IT'S INITIAL LOCATION AND SIZE:
// ------------------------------------------------------------------------------------------------------------
box_destination.w = 100.0f;
box_destination.h = 100.0f;
box_destination.x = screen_width / 2.F - box_destination.w / 2.F; // Center is half the screen width minus half the box width
box_destination.y = screen_height / 2.F - box_destination.h / 2.F; // Center is half the screen height minus half the box height
// Setup the initial beforeTime before the game loop starts up...
// Using double to avoid issues with high performance systems, like my own.
// SDL_GetPerformanceCounter is in a unit of measurement only meaningful to this computer.
// SDL_GetPerformanceFrequency is a value to divide by to convert the counter to seconds.
double before_time = (double)SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency();
double speed = 50.0f; /* pixels per second of movement */
// PRIMARY & EVENT LOOP:
while (!should_quit) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
should_quit = 1;
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_ESCAPE:
should_quit = 1;
break;
}
break;
}
}
// CALCULATE DELTA TIME:
// --------------------------------------------------------------------------------------------------------
// Get the current time by querying the performance counter and using the performance frequency to give it
// meaning
// currentTime is tick counter in seconds.
double current_time = (double)SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency();
// How long it's been since the last frame in seconds.
double delta_time = current_time - before_time;
// Prime beforeTime for the next frame.
before_time = current_time;
// * Use deltaTime from here on for calculations that need to know how long it has been since the last
// frame.
// FRAME RATE (FPS) CALCULATION:
// --------------------------------------------------------------------------------------------------------
// This actually calculates the current framerate in every frame. If you were to display this every frame
// expect it to jitter around since the system isn't going to finish every frame at a perfectly identical
// time to the last. Generally, you would consider adding this to a list of capped size and average the
// result for many frames to smooth the number out.
float current_fps = 1.0 / delta_time;
// In order to filter out some of that jitter more easily, we're just going to show the framerate in the
// window's title bar every so many seconds.
seconds_since_last_fps_update += delta_time;
if (seconds_since_last_fps_update >= update_fps_every_seconds) {
seconds_since_last_fps_update = 0; // Reset the time span since the last frame rate display
// We have to use stringstream magic to keep the original window's title, but tack on the current
// framerate:
snprintf(app_title_ext, max_app_title_ext, "%s (FPS: %.1f)", app_title, current_fps);
SDL_SetWindowTitle(main_window, app_title_ext);
}
// CHECK THE KEYBOARD STATE FOR MOVEMENT KEYS & MOVE THE BOX:
// --------------------------------------------------------------------------------------------------------
// If you had used events for this instead (SDL_KEYDOWN/UP) you would have onlybeen able to act on one key
// press at a time. This also uses min() and max() to keep the box on the screen. The box should not go
// beyond the boundaries of the screen.
double move_amount = speed * delta_time;
const Uint8* state = SDL_GetKeyboardState(NULL);
if (state[SDL_SCANCODE_LEFT]) {
box_destination.x = MAX(box_destination.x - move_amount, 0.0f);
}
if (state[SDL_SCANCODE_RIGHT]) {
box_destination.x = MIN(box_destination.x + move_amount, screen_width - box_destination.w);
}
if (state[SDL_SCANCODE_UP]) {
box_destination.y = MAX(box_destination.y - move_amount, 0.0f);
}
if (state[SDL_SCANCODE_DOWN]) {
box_destination.y = MIN(box_destination.y + move_amount, screen_height - box_destination.h);
}
// RENDERING:
// --------------------------------------------------------------------------------------------------------
// Clear the background:
// Always start fresh and clear, you're never guaranteed to have a blank canvas or the previous rendered
// canvas here.
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
// Render the box:
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // Black with an opacity of 255 (totally visible)
SDL_RenderFillRectF(renderer, &box_destination);
// Show on screen:
SDL_RenderPresent(renderer);
}
// CLEAN-UP & SHUTDOWN:
// ----------------------------------------------------------------------------------------------------------------
return shutdown(main_window, renderer, NULL, NULL);
}
int shutdown(
SDL_Window* main_window,
SDL_Renderer* renderer,
const char* error_prefix,
const char* error
)
{
int result = 0;
if (error) {
// Return non-zero to indicate the application should exit with an error code.
result = -1;
// Output the error to the console, if you have one.
if (error_prefix) printf("%s: %s\n", error_prefix, error);
else printf("%s\n", error);
// This is a simple way to show a message box, if main_window failed to create this will still work
// since main_window will be NULL (the message box will just not have a parent):
SDL_ShowSimpleMessageBox(
SDL_MESSAGEBOX_ERROR,
"Application Execution Failed",
error,
main_window
);
}
if (renderer) SDL_DestroyRenderer(renderer);
if (main_window) SDL_DestroyWindow(main_window);
SDL_Quit();
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment