problems with keeping content displayed with SDL in C

56 views Asked by At

I try to make a game interface using SDL in C language, where the user needs to click on start after the text is displayed so that it takes him to the menu. But when the text and the button is displayed, they disapear leaving the image blank without text or button, but when I click in the area where the button is supposed to be even if it's not appearing in the window, it takes me to the other image.

I don't know how to keep the text and button displayed and wait for the event of click on the "start button" to perform the action I wrote in the code

Here is the code :

#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>

#define SCREEN_WIDTH 1002
#define SCREEN_HEIGHT 584
#define LINE_SPACING 4
#define TEXT_COLOR {0, 0, 0, 0}



// Structure pour représenter une image
typedef struct
{
    SDL_Texture* texture;
    int width;
    int height;
} Image;

// Fonction pour charger une image à partir d'un fichier
Image load_image(SDL_Renderer* renderer, const char* file_path)
{
    SDL_Surface* surface = IMG_Load(file_path);
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_FreeSurface(surface);

    Image image;
    image.texture = texture;
    SDL_QueryTexture(texture, NULL, NULL, &image.width, &image.height);

    return image;
}

// Fonction pour libérer la mémoire occupée par une image
void free_image(Image* image)
{
    SDL_DestroyTexture(image->texture);
}


SDL_Color textColor= {255,255,255,255};
void render_text(SDL_Renderer* renderer, TTF_Font* font, const char* text, SDL_Color color, int x, int y, int delay)
{
    int len = strlen(text);
    char buffer[len + 1];
    memset(buffer, '\0', sizeof(buffer));
    int i = 0;
    bool done = false;
    SDL_Rect rect = { x, y, 0, 0 };
    SDL_Surface* surface = NULL;
    SDL_Texture* texture = NULL;
    Uint32 ticks;

    while (!done)
    {
        ticks = SDL_GetTicks();
        if (i < len)
        {
            strncat(buffer, &text[i], 1);
            surface = TTF_RenderText_Solid(font, buffer, color);
            texture = SDL_CreateTextureFromSurface(renderer, surface);
            rect.w = surface->w;
            rect.h = surface->h;
            SDL_RenderCopy(renderer, texture, NULL, &rect);
            SDL_RenderPresent(renderer);
            i++;
        }
        else
        {
            done = true;
        }
        SDL_Delay(delay - (SDL_GetTicks() - ticks));
    }
    SDL_FreeSurface(surface);
    SDL_DestroyTexture(texture);
}

void Render_text(const char* text, SDL_Rect rect)
{
    SDL_Renderer* renderer = NULL;
    TTF_Font* font = NULL;
    SDL_Surface* surface = TTF_RenderText_Solid(font, text, textColor);
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_FreeSurface(surface);

    int tex_width, tex_height;
    SDL_QueryTexture(texture, NULL, NULL, &tex_width, &tex_height);
    SDL_Rect dstrect = { rect.x + rect.w / 2 - tex_width / 2, rect.y + rect.h / 2 - tex_height / 2, tex_width, tex_height };
    SDL_RenderCopy(renderer, texture, NULL, &dstrect);
    SDL_DestroyTexture(texture);
    TTF_CloseFont(font);
}

void draw_oval(SDL_Renderer* renderer, int x, int y, int radius_x, int radius_y)
{
    const int points = 100;
    const float factor = 2.0f * M_PI / (float)points;
    SDL_Point oval[points];
    for (int i = 0; i < points; i++)
    {
        oval[i].x = x + (int)(radius_x * cosf(i * factor));
        oval[i].y = y + (int)(radius_y * sinf(i * factor));
    }
    SDL_RenderDrawLines(renderer, oval, points);
}

int main(int argc, char* argv[])
{
    SDL_Init(SDL_INIT_VIDEO);
    IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
    TTF_Init();

    SDL_Window* window = SDL_CreateWindow("CONNECT 4",
                                          SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                          SCREEN_WIDTH, SCREEN_HEIGHT, 0);

    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1,
                             SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

    TTF_Font* font1 = TTF_OpenFont("arial.ttf", 29);
    TTF_Font* font2 = TTF_OpenFont("arial.ttf", 35);
    TTF_Font* font = TTF_OpenFont("arial.ttf", 24);

    SDL_Color color = TEXT_COLOR;
    int delay = 50;


    // Charger les images
    Image image1 = load_image(renderer, "back.png");
    Image image2 = load_image(renderer, "hand_close.png");
    Image image3 = load_image(renderer, "handOpen.png");
    Image image4 = load_image(renderer, "menu.png");
    Image image5 = load_image(renderer, "turn.png");
    Image image6 = load_image(renderer, "win.png");

    // Variable pour garder une trace de l'image actuelle
    Image current_image = image1;

    // Effacer l'écran
    SDL_RenderClear(renderer);

    // Afficher l'image actuelle
    SDL_RenderCopy(renderer, current_image.texture, NULL, NULL);

    // Mettre à jour l'écran
    SDL_RenderPresent(renderer);

    //Charger image 2
    SDL_Delay(500);
    current_image = image2;
    // Effacer l'écran
    SDL_RenderClear(renderer);

    // Afficher l'image actuelle
    SDL_RenderCopy(renderer, current_image.texture, NULL, NULL);

    // Mettre à jour l'écran
    SDL_RenderPresent(renderer);

    SDL_Delay(500);


    //charger image 3
    current_image = image3;
    // Effacer l'écran
    SDL_RenderClear(renderer);

    // Afficher l'image actuelle
    SDL_RenderCopy(renderer, current_image.texture, NULL, NULL);

    // Mettre à jour l'écran
    SDL_RenderPresent(renderer);



    SDL_Color color1 = {255, 0, 0, 0};
    render_text(renderer, font1, "You are caught in this castle!", color, 370, 300, delay);
    int y_pos = 350;
    render_text(renderer, font2,"To escape", color, 470, y_pos, delay);
    SDL_RenderPresent(renderer);
    y_pos += 50;
    render_text(renderer, font1, "You need to", color, 470, y_pos, delay);
    y_pos += 50;
    SDL_RenderPresent(renderer);
    render_text(renderer, font2,"WIN this game", color1, 430, y_pos, delay);
    SDL_RenderPresent(renderer);



    // Calcul de la taille du texte "START" pour le centrer dans le bouton
    int oval_width = 100;
    int oval_height = 50;
    int oval_x = (SCREEN_WIDTH - oval_width) /2 +40;
    int oval_y = SCREEN_HEIGHT - oval_height - 10; // mettre une marge de 10 pixels entre le bouton et le bord inférieur de la fenêtre
    SDL_Surface* text_surface = TTF_RenderText_Solid(font, "START", textColor);
    SDL_Texture* text_texture = SDL_CreateTextureFromSurface(renderer, text_surface);
    SDL_Rect text_rect = { oval_x + (oval_width - text_surface->w) / 2, oval_y + (oval_height - text_surface->h) / 2, text_surface->w, text_surface->h };


    // Dessin du bouton ovale
    SDL_SetRenderDrawColor(renderer, 190, 140, 255, 255);

    SDL_Rect oval_rect = { oval_x, oval_y, oval_width, oval_height };

    SDL_RenderFillRect(renderer, &oval_rect);
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderDrawRect(renderer, &oval_rect);

    // Dessin du texte "START" centré dans le bouton
    SDL_RenderCopy(renderer, text_texture, NULL, &text_rect);
    // Affichage du rendu final
    SDL_RenderPresent(renderer);



    SDL_Event event;
    bool done = false;

    while (!done)
    {
        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_QUIT:
                done=true;
                break;
            case SDL_MOUSEBUTTONDOWN:
                // Vérification si le clic de souris est dans la zone du bouton "start"
                if (event.button.x >= oval_x && event.button.x <= oval_x + oval_width && event.button.y >= oval_y && event.button.y <= oval_y + oval_height)
                {
                    if (current_image.texture == image3.texture)
                    {
                        if (event.button.x >= 0 && event.button.x <= 1002 && event.button.y >= 0 && event.button.y <= 584)
                        {
                            current_image = image4;
                            printf("hhh");
                        }
                    }
                    else if (current_image.texture == image4.texture)
                    {
                        if (event.button.x >= 390 && event.button.x <= 610 && event.button.y >= 268 && event.button.y <= 330)
                        {
                            current_image = image5;
                        }
                    }
                    else if (current_image.texture == image5.texture)
                    {
                        if (event.button.x >= 0 && event.button.x <= 1002 && event.button.y >= 0 && event.button.y <= 584)
                        {
                            current_image = image6;
                        }
                    }
                }
                break;
            default:
                break;
            }
        }
        // Effacer l'écran
        SDL_RenderClear(renderer);

        // Afficher l'image actuelle
        SDL_RenderCopy(renderer, current_image.texture, NULL, NULL);

        // Mettre à jour l'écran
        SDL_RenderPresent(renderer);
    }


// Libérer les ressources
    free_image(&image1);
    free_image(&image2);
    free_image(&image3);
    free_image(&image4);
    free_image(&image5);
    free_image(&image6);



    TTF_CloseFont(font);
    TTF_CloseFont(font1);
    TTF_CloseFont(font2);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);

    IMG_Quit();
    TTF_Quit();
    SDL_Quit();

    return 0;
}
1

There are 1 answers

0
RedStoneMatt On

TLDR: The start button doesn't show up because you render it only once before a while loop that clears it and never draws it again.

In your while loop, the only thing you do when no events are raised is:

// Clean the screen
SDL_RenderClear(renderer);

// Render the current image
SDL_RenderCopy(renderer, current_image.texture, NULL, NULL);

// Update the screen
SDL_RenderPresent(renderer);

So on each frame since the while loop has started, these three instructions are the only ones to be executed by your game.

Here are what they do:

  • Clear the current window (essentially making everything on screen black or white)
  • Render the texture contained in current_image.texture
  • Draw the renderer's content onto the window

And at this point of execution, when entering the loop, current_image.texture contains the handOpen.png texture. So it is the only thing to get drawn.

Even if an event is raised during the loop, all your event handling code does is swapping the current image to render.

But at no point do you render your start button again.

You did render it before the while loop:

// Drawing the oval button
SDL_SetRenderDrawColor(renderer, 190, 140, 255, 255);

SDL_Rect oval_rect = { oval_x, oval_y, oval_width, oval_height };

SDL_RenderFillRect(renderer, &oval_rect);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderDrawRect(renderer, &oval_rect);

// Drawing "START" text centered in the button
SDL_RenderCopy(renderer, text_texture, NULL, &text_rect);
// Displaying the final result
SDL_RenderPresent(renderer);

But since you clear the window on each iteration of the while loop and never draw the start button again, it does not show up.

Here is a revised version of your while loop that should hopefully do the work:

SDL_Event event;
bool done = false;
bool showStartButton = true;

while (!done)
{
    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
                done = true;
                break;
            case SDL_MOUSEBUTTONDOWN:
                // Checking if the mouse click is in the "start" button's area
                if (event.button.x >= oval_x && event.button.x <= oval_x + oval_width && event.button.y >= oval_y && event.button.y <= oval_y + oval_height)
                {
                    if (current_image.texture == image3.texture)
                    {
                        if (event.button.x >= 0 && event.button.x <= 1002 && event.button.y >= 0 && event.button.y <= 584)
                        {
                            current_image = image4;
                            showStartButton = false;
                        }
                    }
                    else if (current_image.texture == image4.texture)
                    {
                        if (event.button.x >= 390 && event.button.x <= 610 && event.button.y >= 268 && event.button.y <= 330)
                        {
                            current_image = image5;
                        }
                    }
                    else if (current_image.texture == image5.texture)
                    {
                        if (event.button.x >= 0 && event.button.x <= 1002 && event.button.y >= 0 && event.button.y <= 584)
                        {
                            current_image = image6;
                        }
                    }
                }
                break;
            default:
                break;
        }
    }
    // Clean the screen
    SDL_RenderClear(renderer);

    // Render the current image
    SDL_RenderCopy(renderer, current_image.texture, NULL, NULL);

    if (showStartButton) {
        // Drawing the oval button
        SDL_SetRenderDrawColor(renderer, 190, 140, 255, 255);

        SDL_Rect oval_rect = { oval_x, oval_y, oval_width, oval_height };

        SDL_RenderFillRect(renderer, &oval_rect);
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderDrawRect(renderer, &oval_rect);

        // Drawing "START" text centered in the button
        SDL_RenderCopy(renderer, text_texture, NULL, &text_rect);
    }

    // Update the screen
    SDL_RenderPresent(renderer);
}