Search code examples
csdlsdl-2

Why is SDL_Rect* array not functioning as intended?


I'm just trying to write a program to draw to a window using SDL. I'm trying to use rects to draw, and I thought that if I just don't clear the screen then I could leave a trail of where a rect has been whilst changing its position. However this causes the window to glitch out like mad and so I came up with another solution wherein I would have an array of Rects that is added to as the program draws and then each frame it will go through the array and render each rect to the window in the correct place. However, when trying to render the rects from the array it seems that they are all at the same position, and not in the position they were set to before being added to the array?

#include <SDL2/SDL.h>

#define SCREEN_WIDTH 1920
#define SCREEN_HEIGHT 1080

SDL_Rect *rects[200] = {};

int get_rect_count()
{
    int i;
    for (i = 0; i < sizeof(rects) / sizeof(rects[0]); i++)
    {
        if (rects[i] == NULL)
            break;
    }
    return i;
}

void draw_rects(SDL_Renderer *renderer)
{
    SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
    SDL_RenderClear(renderer);
    SDL_SetRenderDrawColor(renderer, 241, 194, 125, 255);
    for (int i = 0; i < get_rect_count(); i++)
    {
        SDL_RenderDrawRect(renderer, rects[i]);
        SDL_RenderFillRect(renderer, rects[i]);
    }
    SDL_RenderPresent(renderer);
}

void ball(int sp_x, int sp_y, SDL_Renderer *renderer)
{
    for (int i = 0; i < 3; i++)
    {
        SDL_Rect rect;
        int offset = i * 10;
        rect.x = sp_x + offset;
        rect.y = sp_y - offset;
        rect.w = 10;
        rect.h = 10;
        rects[i] = &rect;
        draw_rects(renderer);
        SDL_Delay(200);
    }
}

int main(int argc, char *args[])
{
    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;

    if (SDL_Init(SDL_INIT_VIDEO) < 0)
        printf("sdl failed lol");
    else
    {
        window = SDL_CreateWindow("window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
        SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
        SDL_RenderClear(renderer);
        SDL_RenderPresent(renderer);
        SDL_Delay(750);
        ball(SCREEN_WIDTH / 8, SCREEN_HEIGHT * 0.75, renderer);
        SDL_Delay(1000);
    }
    return 0;
}

Solution

  • One major problem is here:

    for (int i = 0; i < 3; i++)
    {
        SDL_Rect rect;
    

    Here the variable rect is local inside the loop. Once the loop iterates, the life-time of rect ends and it ceases to exist. Any pointers to it that you have saved will become invalid, and dereferencing these pointers will lead to undefined behavior.

    Now that's the theory. What probably happens in reality is that the compiler will reuse the memory for rect, meaning that all pointers will be to one single SDL_Rect structure, which you overwrite each iteration in the loop. So you end up with all rectangles being set to the values of the very last rectangle.

    One possible way to solve your problem is to allocate rectangles dynamically using malloc. You of course have to remember to free them once you're finished.

    Another solution is to use an array of SDL_Rect structure objects, not pointers, and keep track of its "size" (number of used elements) with another variable.