Skip to content

Instantly share code, notes, and snippets.

@xeekworx
Last active January 5, 2023 23:01
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xeekworx/8d92a55563c7b51378a34e98d027bd39 to your computer and use it in GitHub Desktop.
Save xeekworx/8d92a55563c7b51378a34e98d027bd39 to your computer and use it in GitHub Desktop.
SDL Renderer & Keyboard State Example
#include <SDL.h>
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>
#include <format>
int main(int argc, char* argv[])
{
std::string app_title = "SDL Box Movement Example - Use the arrow keys!";
int screen_width = 1024, screen_height = 768;
SDL_Window* main_window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Event event = { 0 };
bool should_quit = false;
SDL_FRect box_destination = { }; // Use SDL_FRect instead of SDL_Rect for subpixel rendering.
std::stringstream error; // Aid in creating error strings for output later.
std::stringstream update_title; // Aid in appending information to the window's title.
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.
try {
// INITIALIZATION:
// ------------------------------------------------------------------------------------------------------------
// Initialize SDL and create a window and renderer
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
error << "Failed to initialize SDL: " << SDL_GetError();
throw(error.str());
}
if ((main_window = SDL_CreateWindow(
app_title.c_str(),
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) {
error << "Failed to create a window: " << SDL_GetError();
throw(error.str());
}
// -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) {
error << "Failed to create the renderer: " << SDL_GetError();
throw(error.str());
}
// 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 = static_cast<double>(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:
update_title.str("");
update_title << app_title << " (FPS: " << current_fps << ")";
SDL_SetWindowTitle(main_window, update_title.str().c_str());
}
// 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.
float move_amount = static_cast<float>(speed * delta_time);
const Uint8* state = SDL_GetKeyboardState(NULL);
if (state[SDL_SCANCODE_LEFT]) {
box_destination.x = std::max(box_destination.x - move_amount, 0.0f);
}
if (state[SDL_SCANCODE_RIGHT]) {
box_destination.x = std::min(box_destination.x + move_amount, screen_width - box_destination.w);
}
if (state[SDL_SCANCODE_UP]) {
box_destination.y = std::max(box_destination.y - move_amount, 0.0f);
}
if (state[SDL_SCANCODE_DOWN]) {
box_destination.y = std::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);
}
}
catch (std::string error_str) {
// 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, app_title.c_str(), error_str.c_str(), main_window);
// Output the error to the console, if you have one
std::cout << error_str << std::endl;
}
// CLEAN-UP & SHUTDOWN:
// ----------------------------------------------------------------------------------------------------------------
if (renderer) SDL_DestroyRenderer(renderer);
if (main_window) SDL_DestroyWindow(main_window);
SDL_Quit();
return 0;
}
@jtullos-bt
Copy link

I wrote this to help others, I consider this Public Domain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment