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_vals() 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)
// 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_vals(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.
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
// Function prototypes
int init_window(void); // Create window and renderer
void init_vals(void); // Initialize values
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 values
init_vals();
// 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_vals(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 frect = { (WINDOW_WIDTH-160)/2, (WINDOW_HEIGHT-120)/2, 160, 120 };
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255); // Set the color to fill rectangle.
SDL_RenderFillRect(renderer, &frect); // 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();
}