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

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

Ana sayfa > Oyun programlama > SDL3 programming > Animation with texture stream

Animation with texture stream

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:

  1. The global variables shown below are first defined at the beginning of the program:
    • SDL_Texture *texture_back: Background texture
    • SDL_Texture *texture_stream: Stream texture
    • SDL_Surface *surface_images: Surface images
    • SDL_Surface *surface_image: Surface image
    • unsigned int last_frame_time: Last frame time
    • void* pixels_locked: Locked pixel pointer
    • int pitch_locked: Locked pitch
    • A variable named player is defined from the following structure data type:
      struct players {
        float x;
        float y;
        float width;
        float height;
        float vel_x;
        float vel_y;
        int frame;
      } player;
      
  2. Initial values ​​are assigned to global variables with the init_vars() function.
  3. In the load_img() function,
    • A texture is created for the background with the IMG_LoadTexture() function.
    • A .png image is loaded for the player images with the IMG_Load() function, and a surface named surface_images is created with the SDL_ConvertSurface() function.
  4. In the process_event() function, the player.vel_x variable value is increased or decreased by 20 with the up and down arrow keys.
  5. In the update_screen() function,
    • The difference between the active time value and the last_frame_time variable value obtained with the SDL_GetTicks() function is divided by 1000, and the resulting value is It is assigned to the delta_time variable.
    • The active time value is taken in milliseconds with the SDL_GetTicks() function and assigned to the last_frame_time variable to be used in the next run of the program's main loop.
    • If the sum of the multiplication of the player.x value, which shows the horizontal coordinate of the image moving in the main window, and the delta_time variable value with the player.vel_x variable value is smaller than the main window width, the multiplication of the delta_time variable value and the player.vel_x variable value is assigned to the player.x variable. Otherwise, the player.x and player.frame variables are assigned the value 0.
    • When the player.x value reaches a multiple of 12,
      • If the player.frame variable value is 7, it is 0, otherwise, it is one more than its own value. Thus, 8 images are processed in order.
      • A surface with the player variable dimensions is created with the SDL_CreateSurface() function.
      • The texture_stream is locked with the SDL_LockTexture() function.
      • A surface is created with the rect_src sized section of the surface_images surface with the SDL_BlitSurface() function and assigned to the surface_image variable.
      • The pixel values ​​of the surface_image surface are copied to the texture_stream variable via the pixels_locked variable with the memcpy() function.
      • The texture_stream lock is unlocked with the SDL_UnlockTexture() function.
  6. Within the draw_screen() function,
    • The texture_back variable is loaded to the main window with the SDL_RenderTexture() function.
    • The texture_stream variable is loaded into the main window with the SDL_RenderTexture() function.
    • The x and y coordinates of the player variable and the frame index value and speed value are written to the main window title.

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