In this section, we will examine a program that automatically loads 8 different images of the same human drawing in different stages of walking into the window in order, created from a .png image file called surface_images.
The animation performed with the texture created directly from an image file with the IMG_LoadTexture() function in the previous section is performed here via surfaces via a stream texture created with the SDL_CreateTexture() function.
When the program runs, the following operations are performed in addition to the basic operations:
struct players { float x; float y; float width; float height; float vel_x; float vel_y; int frame; } player;
main.c file content will be as follows:
#include <stdio.h>
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.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_Texture *texture_back = NULL; // Background texture
SDL_Texture *texture_stream = NULL; // Foreground texture
SDL_Surface *surface_images = NULL; // Surface images
SDL_Surface *surface_image = NULL; // Surface image
SDL_Renderer *renderer = NULL; // Renderer variable
unsigned int last_frame_time; // Last frame time
void* pixels_locked = NULL; // Locked pixels pointer
int pitch_locked; // Locked pitch
// Struct declaration
struct players {
float x;
float y;
float width;
float height;
float vel_x;
float vel_y;
int frame;
} player;
// Function prototypes
int init_window(void); // Create window and renderer
void init_vars(void); // Initialize variables
void load_img(void); // Load image to texture
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();
// Load .png file to texture
load_img();
// 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 window", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
return false;
}
texture_stream = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, 88, 110);
if(texture_stream == NULL) {
SDL_Log("Can not create stream texture! SDL Error: %s\n", SDL_GetError());
return false;
}
return true;
}
// Initialization function that runs only once at the beginning of the program
void init_vars(void)
{
last_frame_time = 0;
pitch_locked = 0;
player.x = 0;
player.y = 310;
player.width = 88;
player.height = 110;
player.vel_x = 100;
player.vel_y = 100;
player.frame = 0;
}
// Load image to texture
void load_img(void)
{
// Loading image to background texture
texture_back = IMG_LoadTexture(renderer, "background.png");
if(texture_back == NULL) {
SDL_Log("IMG_LoadTexture error: %s\n", SDL_GetError());
}
// Loading player images to surface
surface_images = IMG_Load("player.png");
if(surface_images == NULL) {
SDL_Log("IMG_Load error: %s\n", SDL_GetError());
}
surface_images = SDL_ConvertSurface(surface_images, SDL_PIXELFORMAT_RGBA8888);
}
// 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: // Key pressed.
switch(event.key.key) {
case SDLK_UP: if(player.vel_x<1000) player.vel_x += 20; break;
case SDLK_DOWN: if(player.vel_x>0) player.vel_x -= 20; break;
case SDLK_ESCAPE: is_running = false; break;
}
break;
}
}
}
// Updates objects in the main window
void update_screen(void)
{
// Get the difference between the active time and the previous time of loop in seconds
float time_delta = (SDL_GetTicks() - last_frame_time) / 1000.0;
// Assign the active time to use in the next iteration of the loop
last_frame_time = SDL_GetTicks();
// Calculate moving size
// player.vel_x assigned to move_x variable.
float move_x = player.vel_x * time_delta;
// If player.x plus move_x, player.x increased by move_x
if(player.x + move_x < WINDOW_WIDTH) {
player.x += move_x;
if((int)player.x%12==0) {
player.frame = player.frame==7 ? 0 : player.frame+1;
surface_image = SDL_CreateSurface(player.width, player.height, SDL_PIXELFORMAT_RGBA8888);
SDL_LockTexture(texture_stream, NULL, &pixels_locked, &pitch_locked);
SDL_Rect rect_src = { player.frame * player.width, 0, player.width, player.height };
SDL_BlitSurface(surface_images, &rect_src, surface_image, NULL);
memcpy(pixels_locked, surface_image->pixels, pitch_locked * player.height);
SDL_UnlockTexture(texture_stream);
}
}
else {
player.x = 0;
player.frame = 0;
}
}
// 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.
SDL_RenderTexture(renderer, texture_back, NULL, NULL);
SDL_FRect frect_src = { 0, 0, player.width, player.height };
SDL_FRect frect_dst = { (int) player.x, (int) player.y, (int) player.width, (int) player.height };
SDL_RenderTexture(renderer, texture_stream, &frect_src, &frect_dst);
// 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
char cdizi[100];
sprintf(cdizi, "x: %d y: %d Frame: %d Speed: %.2f", (int) player.x, (int) player.y, player.frame, player.vel_x);
SDL_SetWindowTitle(window, cdizi);
}
// Destroy Renderer and SDL window, exit from SDL3
void destroy_window(void)
{
SDL_DestroyTexture(texture_stream);
SDL_DestroyTexture(texture_back);
SDL_DestroySurface(surface_image);
SDL_DestroySurface(surface_images);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}