In this section, we will try to examine the concept of frame in details. In game programming, each time the screen updates is called a frame.In the C programming language, when we create the game's main loop with a while loop, each iteration of this loop is usually considered a frame.
We will add the following variables and the init_vars() and update_screen() function contents to the program that creates a 160 x 120 pixel rectangle in the middle of the main window. Instead of the SDL_CreateWindow() and SDL_CreateRenderer() functions, we will create the program's main window and renderer using only the SDL_CreateWindowAndRenderer() function.
#include <stdio.h> // Header file (for sprintf() function)
unsigned int time_start; // Start time
int frame_no; // Number of frames (number of times the while loop repeats)
Uint64 last_frame_time; // Last frame time (in nanoseconds)
Uint64 frame_period_avg; // Average frame time (in nanoseconds)
Uint8 random_r=0, random_g=255, random_b=255; // Rectangle color values
// Create a window and a 2D rendering context for the window.
if(!SDL_CreateWindowAndRenderer("SDL3 window", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
return false;
}
void init_vars(void)
{
// Global variables initialization
time_start = SDL_GetTicks();
frame_no = 0;
last_frame_time = 0;
frame_period_avg = 0;
}
void update_screen(void)
{
// The difference between the time value in the previous iteration of the while loop and
// the current time value in nanoseconds (while loop repeat time).
Uint64 frame_period = SDL_GetTicksNS() - last_frame_time;
// The current time value in nanoseconds.
last_frame_time = SDL_GetTicksNS();
char cdizi[200];
if(frame_no<1001) frame_period_avg += frame_period;
else if(frame_no==1001) frame_period_avg /= 1000;
// Time in seconds since the program started running.
float time_elapsed = (SDL_GetTicks() - time_start) / 1000.f;
// Average frame per second
float avg_fps = frame_no / time_elapsed;
sprintf(cdizi, "Frame: %d - Frame period: %I64d - AFP: %I64d - FPS: %.2f",
frame_no, frame_period, frame_period_avg, avg_fps);
SDL_SetWindowTitle(window, cdizi);
frame_no++;
}
When the program runs,
The reason for calculating the average frame period is that each frame value varies.
When we run the program, the rectangle at the top of the main window might seem to be drawn only once, but in fact, the rectangle is redrawn with the draw_screen() function at each iteration of the while loop, the program's main loop that runs endlessly, unless the program is exited.
Since the color of the rectangle in the center of the main window changes every 50 increments of the frame_no value, it's easy to see that the rectangle is redrawn not just once, but with each iteration of the loop.
main.c file content will be as follows:
#include <stdio.h>
#include <SDL3/SDL.h>
// Window width and height
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
// Global variables
int is_running = false; // Variable that controls the execution of the main loop of the program
SDL_Window *window = NULL; // Main window variable
SDL_Renderer *renderer = NULL; // Renderer variable
unsigned int time_start; // Start time
int frame_no; // Frame number
Uint64 last_frame_time; // Last frame time
Uint64 frame_period_avg; // Average frame period
Uint8 random_r=0, random_g=255, random_b=255; // Rectangle color values
// Function prototypes
int init_window(void); // Create window and renderer
void init_vars(void); // Initialize variables
void process_event(void); // Process events
void update_screen(); // Update values
void draw_screen(void); // Draw screen
void destroy_window(void); // Destroy window
int main(int argc, char* argv[])
{
// Create window and renderer
is_running = init_window();
// Initialize variables
init_vars();
// Main loop
while (is_running) {
process_event();// Processing SDL events (Here keyboard inputs)
update_screen(); // Updating variables
draw_screen(); // Drawing objects on the window (Rendering)
}
// Destroy renderer and SDL window
destroy_window();
return 0;
}
// Create window and renderer
int init_window(void)
{
// Initialize the SDL library.
if(SDL_Init(SDL_INIT_VIDEO) == false) {
SDL_Log("SDL init error: %s\n", SDL_GetError());
return false;
}
// Create a window and a 2D rendering context for the window.
if(!SDL_CreateWindowAndRenderer("SDL3 penceresi", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
return false;
}
return true;
}
// Initialization function that runs only once at the beginning of the program
void init_vars(void)
{
time_start = SDL_GetTicks();
frame_no = 0;
last_frame_time = 0;
frame_period_avg = 0;
}
// Function to control SDL events and process keyboard inputs
void process_event(void)
{
SDL_Event event;
// Creating a loop to process user inputs
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_EVENT_QUIT: // Logout action by the user (x button at the top right of the window)
is_running = false; // Terminates the execution of the program main loop.
break;
case SDL_EVENT_KEY_DOWN: // Indicate that a key was pressed.
if(event.key.key == SDLK_ESCAPE) { // Esc key pressed
is_running = false; // Terminates the execution of the program main loop.
}
break;
}
}
}
void update_screen(void)
{
// The difference between the time value in the previous iteration of the while loop and
// the current time value in nanoseconds (while loop repeat time).
Uint64 frame_period = SDL_GetTicksNS() - last_frame_time;
// The current time value in nanoseconds.
last_frame_time = SDL_GetTicksNS();
char cdizi[200];
if(frame_no<1001) frame_period_avg += frame_period;
else if(frame_no==1001) frame_period_avg /= 1000;
// Time in seconds since the program started running.
float time_elapsed = (SDL_GetTicks() - time_start) / 1000.f;
// Average frame per second
float avg_fps = frame_no / time_elapsed;
sprintf(cdizi, "Frame: %d - Frame period: %I64d - AFP: %I64d - FPS: %.2f",
frame_no, frame_period, frame_period_avg, avg_fps);
SDL_SetWindowTitle(window, cdizi);
frame_no++;
}
// Render function used to draw game objects in the main window
void draw_screen(void)
{
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // Set the color used for drawing operations.
SDL_RenderClear(renderer); // Clear the current rendering target with the drawing color.
// Setting rectangle dimensions
SDL_FRect frect1 = { (WINDOW_WIDTH-160)/2, 20, 160, 120 };
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255); // Set the color to fill rectangle.
SDL_RenderFillRect(renderer, &frect1); // Fill a rectangle on the current rendering target with the drawing color at subpixel precision.
if (frame_no%50==0) {
random_r = rand() % 256;
random_g = rand() % 256;
random_b = rand() % 256;
}
// Setting rectangle dimensions
SDL_FRect frect2 = { (WINDOW_WIDTH-160)/2, (WINDOW_HEIGHT-120)/2, 160, 120 };
SDL_SetRenderDrawColor(renderer, random_r, random_g, random_b, 255); // Set the color to fill rectangle.
SDL_RenderFillRect(renderer, &frect2); // Fill a rectangle on the current rendering target with the drawing color at subpixel precision.
// Loading all objects drawn one by one to the back buffer to the front buffer at once and loading them onto the screen
// This process prevents each drawn object from being displayed on the screen one by one.
SDL_RenderPresent(renderer); // Update on screen all operations performed since the previous call
}
// Destroy Renderer and SDL window, exit from SDL3
void destroy_window(void)
{
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}