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 > Collision detection

Collision detection

In this section, we will examine a program that moves a 20-pixel-sized ball up, down, left, right, left up, right up, left down, and right down using the arrow keys, using the edges of the main window and a rectangle in the middle of the main window with a width of 200 pixels and a height of 300 pixels as the border, changes the blocking status of the rectangle with the R key, and changes the speed with the PageUp and PageDown keys.

When the program runs, the following operations are performed in addition to the basic operations:

  1. At the beginning of the program, the following global variables are first defined:
    • unsigned int last_frame_time: Last frame time
    • SDL_FRect frect_col: Rectangle variable
    • int move_size: Move size variable
    • bool rect_enabled: Rectangle state variable
    • A variable named ball is defined from the following structure data type:
      struct objects {
        float x;
        float y;
        float width;
        float height;
        float vel_x;
        float vel_y;
      } ball;
      
  2. The init_vars() function assigns initial values ​​to global variables.
  3. In the load_img() function, an image file for the ball is loaded into the texture variable with the IMG_LoadTexture() function.
  4. In the process_event() function,
    • When the arrow keys are pressed, the move_size variable value in the direction the arrow points to is added to the ball.vel_x or ball.vel_y variable, which has a value of 0. Thus, movement is achieved.
    • When the arrow keys are released, the move_size variable value in the direction the arrow points to is subtracted from the ball.vel_x or ball.vel_y variable, and the values ​​of these variables are set to 0. Thus, the movement ends.
    • When the R key is pressed, the obstruction status of the rectangle is changed.
    • The speed value of the ball is changed with the PageUp and PageDown keys.
  5. In update_screen() function,
    • A variable named rect_control is defined, which contains the latest coordinates and size of the ball.
    • A variable named rect_col is defined, which contains the coordinates and size of the rectangle.
    • The difference between the active time and the previous time of the loop is taken in seconds and assigned to the time_delta variable.
    • The active time is taken with the SDL_GetTicks() function and assigned to the last_frame_time variable to be used in the next iteration of the loop.
    • The product of ball.vel_x and the loop iteration time (time_delta) is assigned to the move_x variable
    • The ball.x value is incremented by the move_x value.
    • The ball.x value is assigned to the rect_control.x variable.
    • If ball.x is less than 0 or the sum of ball.x and ball.width is greater than the width of the window or rect_enabled is true and the ball and the rectangle intersect in the control made with the SDL_HasRectIntersection() function, the ball.x and rect_control.x variables take their previous values.
    • The product of ball.vel_y and the loop iteration time (time_delta) is assigned to the move_y variable.
    • The ball.y value is incremented by the move_y value.
    • The ball.y value is assigned to the rect_control.y variable.
    • If ball.y is less than 0 or the sum of ball.y and ball.height is greater than the height of the window or rect_enabled is true and If the ball and the rectangle intersect in the control made with the SDL_HasRectIntersection() function, the ball.y and rect_control.y variables take their previous values.
    • The coordinates and speed values ​​of the ball and the obstacle status of the rectangle are written to the main window title.
  6. In the draw_screen() function,
    • With the SDL_RenderRect() function, the rectangle is drawn to the main window in red if the obstacle status is active, and in green if not.
    • With the SDL_RenderTexture() function, the ball is displayed in its position in the main window.

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 = NULL;   // Texture variable
SDL_Renderer *renderer = NULL; // Renderer variable

unsigned int last_frame_time;  // Last frame time
SDL_FRect frect_col;           // Rectangle variable
int move_size;                 // Move size
bool rect_enabled;             // Rectangle state variable

// Struct declaration
struct objects {
    float x;
    float y;
    float width;
    float height;
    float vel_x;
    float vel_y;
} ball;

// 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;
    }

    return true;
}

// Initialization function that runs only once at the beginning of the program
void init_vars(void)
{
    last_frame_time = 0;
    move_size = 200;
    rect_enabled = true;
    frect_col.x = 220.00;
    frect_col.y = 90.00;
    frect_col.w = 200.00;
    frect_col.h = 300.00;

    ball.x = 0;
    ball.y = 0;
    ball.width = 20;
    ball.height = 20;
    ball.vel_x = 0;
    ball.vel_y = 0;
}

// Load image to texture
void load_img(void)
{
    // Loading image to texture
	texture = IMG_LoadTexture(renderer, "ball.png");

	if(texture == NULL) {
       SDL_Log("IMG_LoadTexture error: %s\n", SDL_GetError());
	}
}

// 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.
               if(event.key.repeat == 0) {
                  switch(event.key.key) {
                     case SDLK_UP: ball.vel_y -= move_size; break;
                     case SDLK_DOWN: ball.vel_y += move_size; break;
                     case SDLK_LEFT: ball.vel_x -= move_size; break;
                     case SDLK_RIGHT: ball.vel_x += move_size; break;
                     case SDLK_R: rect_enabled = !rect_enabled; break;
                     case SDLK_PAGEUP: if(move_size<1000) move_size += 20; break;
                     case SDLK_PAGEDOWN: if(move_size>0) move_size -= 20; break;
                     case SDLK_ESCAPE: is_running = false; break;
                 }
               }
               break;

          case SDL_EVENT_KEY_UP: // Key released.
               if(event.key.repeat == 0) {
                  switch(event.key.key) {
                     case SDLK_UP: ball.vel_y += move_size; break;
                     case SDLK_DOWN: ball.vel_y -= move_size; break;
                     case SDLK_LEFT: ball.vel_x += move_size; break;
                     case SDLK_RIGHT: ball.vel_x -= move_size; break;
                  }
               }
               break;
       }
    }
}

// Updates objects in the main window
void update_screen(void)
{
    SDL_Rect rect_control = { ball.x, ball.y, 20, 20 };
    SDL_Rect rect_col = { 220, 90, 200, 300};

    // 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
    // Multiplication of ball.vel_x and the loop repeat time (delta time) assigned to move_x variable.
    float move_x = ball.vel_x * time_delta;

    // Ball x coordinate increased by move_x
    ball.x += move_x;
    rect_control.x = ball.x;
    // If ball.x is lower than 0 or, sum of ball.x and ball.width is greater than windows width
    // ball.x gets its previous value.
    if((ball.x<0) || (ball.x + ball.width > WINDOW_WIDTH) || (rect_enabled && SDL_HasRectIntersection(&rect_control, &rect_col))) {
	   ball.x -= move_x;
	   rect_control.x = ball.x;
    }

    // Multiplication of ball.vel_y and the loop repeat time (delta time), assigned to move_y variable.
    float move_y = ball.vel_y * time_delta;

    // Ball y coordinate increased by move_y
    ball.y += move_y;
    rect_control.y = ball.y;
    // If ball.yx is lower than 0 or, sum of ball.y and ball.height is greater than windows height
    // ball.y gets its previous value.
    if((ball.y < 0) || (ball.y + ball.height > WINDOW_HEIGHT) || (rect_enabled && SDL_HasRectIntersection(&rect_control, &rect_col))) {
       ball.y -= move_y;
	   rect_control.y = ball.y;
    }

    char cdizi[120];
    sprintf(cdizi, "ball.x: %.2f ball.y: %.2f ball.vel_x: %.2f ball.vel_y: %.2f - Rectangle: %s",
                    ball.x, ball.y, ball.vel_x, ball.vel_y, rect_enabled ? "Enabled" : "Disabled");
    SDL_SetWindowTitle(window, cdizi);
}

// 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_SetRenderDrawColor(renderer, rect_enabled ? 255 : 0, !rect_enabled ? 255 : 0, 0, 255);
    SDL_RenderRect(renderer, &frect_col);

    SDL_FRect rect_dst = { ball.x, ball.y, 20, 20 };
    // Load texture content to main window
    SDL_RenderTexture(renderer, texture, NULL, &rect_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
}

// Destroy Renderer and SDL window, exit from SDL3
void destroy_window(void)
{
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}