Warning: Undefined array key "HTTP_ACCEPT_LANGUAGE" in /var/www/vhosts/bilgigunlugum.net/httpdocs/index.php on line 43
SDL Oyun Programlama

SDL3 Oyun Programlama sayfalarımız yayında...

Ana sayfa > Oyun programlama > SDL3 programming > Frame concept

Frame concept

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,

  1. In addition to the operations explained in the previous section, the following operations occur:
  2. At program startup, the following global variables are defined:
    • unsigned int time_start: Program start time
    • int frame_no: Frame count (number of times the while loop is repeated).
    • Uint64 last_frame_time: Last frame time value (in nanoseconds).
    • Uint64 frame_period_avg: Average frame duration (in nanoseconds).
  3. In the init_window() function, the program main window and renderer are created with the SDL_CreateWindowAndRenderer() function.
  4. By running the init_vals() function, the value obtained with the SDL_GetTicks() function is assigned to the time_start variable, and the value 0 is assigned to the frame_no, last_frame_time and frame_period_avg variables.
  5. By running the update_screen() function,
    • The difference between the active time value obtained in nanoseconds with the SDL_GetTicksNS() function and the time value obtained in the previous iteration of the main while loop is taken and assigned to the frame_period variable. This value shows the time taken by one iteration of the while loop and also the time elapsed between frames.
    • The active time value obtained in nanoseconds with the SDL_GetTicksNS() function is assigned to the last_frame_time variable to be used in the next iteration of the loop.
    • The obtained frame_period value is added to the frame_period_avg variable value until the loop repeats 1000 times. When a total of 1000 frames are processed, the loop repeats 1000 times and divides the frame_period_avg value by 1000 to calculate the average frame period for 1000 frames.

      The reason for calculating the average frame period is that each frame value varies.

    • The difference between the active time value obtained in milliseconds with the SDL_GetTicks() function and the time_start variable value is divided by 1000 to obtain the time elapsed since the program started working. The frame_no variable value, which indicates the number of frames processed since the program started working, is divided by this value to obtain the Frame Per Second (FPS) value and assigned to the avg_fps variable.
    • The SDL_SetWindowTitle() function writes the frame_no, frame_period, frame_period_avg and avg_fps variable values ​​to the main window title.
    • Increases the frame_no variable value by 1.

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();
}