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 > Continuous collision detection

Continuous collision detection

In this section, we will examine a program that automatically moves a 20-pixel-sized ball by accepting the edges of the main window and the edges of a rectangle in the middle of the main window as borders, with a width of 200 pixels and a height of 300 pixels, reverses the movement in the opposite direction with collision detection in the border areas, changes the obstacle status of the rectangle with the R key, and changes the speed with the up and down arrow 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: Movement size variable
    • bool rect_enabled: Rectangle state variable
    • bool x_vel, y_vel: Speed ​​increase control variables
    • 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. 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 process_event()function,
    • When the up and down arrow keys are pressed, if the x_vel variable for the x direction and the y_vel variable for the y direction are true, the ball's speed is increased or decreased.
    • When the X key is pressed, the x_vel variable and when the Y key is pressed, the y_vel variable are inversed, enabling or disabling the speed change in the relevant direction.
    • When the R key is pressed, the obstruction status of the rectangle is changed.
  5. In update_screen() function,
    • A variable named rect_control is defined that contains the latest coordinates and size of the ball.
    • A variable named rect_col is defined that 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 retrieved 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 the width of the window If the ball and the rectangle intersect when checked with the SDL_HasRectIntersection() function, the ball.x and rect_control.x variables return to their previous values ​​and the speed direction is reversed.
    • 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 increased 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 the ball and the rectangle intersect when checked with the SDL_HasRectIntersection() function, the ball.y and rect_control.y variables return to their previous values ​​and the speed direction is reversed.
    • The coordinates and speed values ​​of the ball and the obstacle status of the rectangle are displayed in the main window title.
  6. In draw_screen() function,
    • With the SDL_RenderRect() function, the rectangle is drawn to the main window, red if the obstacle condition is active, 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 <stdlib.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
bool x_vel, y_vel;             // Velocity change enabled variables

// 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;
    x_vel = true;
    y_vel = 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 = 200;
    ball.vel_y = 200;
}

// 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.
               switch(event.key.key) {
                  case SDLK_UP:
                       if(abs(ball.vel_x)<1000.00 && x_vel) {
                          ball.vel_x += (ball.vel_x>0) ? 10.00 : -10.00;
                       }
                       if(abs(ball.vel_y)<1000.00 && y_vel) {
                          ball.vel_y += (ball.vel_y>0) ? 10.00 : -10.00;
                       }
                       break;
                  case SDLK_DOWN:
                       if(abs(ball.vel_x)>0.00 && x_vel) {
                          ball.vel_x += (ball.vel_x>0) ? -10.00 : 10.00;
                       }
                       if(abs(ball.vel_y)>0.00 && y_vel) {
                          ball.vel_y += (ball.vel_y>0) ? -10.00 : 10.00;
                       }
                       break;
                  case SDLK_X: x_vel = !x_vel; break;
                  case SDLK_Y: y_vel = !y_vel; break;
                  case SDLK_R: rect_enabled = !rect_enabled; break;
                  case SDLK_ESCAPE: is_running = false; 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;
	   ball.vel_x = -ball.vel_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;
       ball.vel_y = -ball.vel_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();
}