SDL2 switch animation

i dont know how to make an animation appears in the SDL window once the previous animation finishes , can you help me please by sending a code that shows me how’s gonna be made ?

Hello and welcome to the forum!

It’s always good to show the code that you have so far. Show what you have made, how you’re currently animating graphics and so on, so the members here know where you are in the process.

I’m gonna assume that when you write animation, you’re referring to sprite animations containing two- or more frames that you’re iterating through to animate a graphical object on the screen?

hello , thank you for answering me , so i did a first animation for my game as initialization with an image that goes from the bottom of the window SDL till the top with audio sound , i wanted to clear the window so i can do another animation which contains an image as background and a text with buttons as a menu but i couldn’t make it , here is the source code because i cant send a zip file containing the image, font and audio sound i’ve used, thank you for helping me :smiling_face_with_three_hearts: :heart:
`#include <stdio.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#include <SDL2/SDL_ttf.h>

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int SCROLL_SPEED = 2;
const int ANIMATION_DURATION = 1000/30; // Durée de l’animation en millisecondes (5 secondes)

SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Texture* imageTexture = NULL;
Mix_Music* music = NULL;
TTF_Font* font = NULL;

void cleanup() {
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
TTF_CloseFont(font);
TTF_Quit();
IMG_Quit();
SDL_Quit();
}
int audioThread(void* data) {
// Code pour l’audio
Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048);
Mix_Music* music = Mix_LoadMUS(“initjeuaudio.mp3”);
Mix_PlayMusic(music, 0); // -1 for looping, 0 for playing once
while (Mix_PlayingMusic()) {
SDL_Delay(1000/60);
}
Mix_FreeMusic(music);
Mix_CloseAudio();
Mix_Quit();

return 0;

}
int main(int argc, char* argv) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)&& Mix_Init(MIX_INIT_MP3) != 0) {
printf(“SDL could not initialize! SDL_Error: %s\n”, SDL_GetError());
return -1;
}

window = SDL_CreateWindow("SDL Animation", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!window) {
    printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
    cleanup();
    return -1;
}

renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (!renderer) {
    printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError());
    cleanup();
    return -1;
}

SDL_Surface* imageSurface = IMG_Load("hello.png");
if (!imageSurface) {
    printf("Unable to load image! SDL_Error: %s\n", SDL_GetError());
    cleanup();
    return -1;
}

imageTexture = SDL_CreateTextureFromSurface(renderer, imageSurface);
SDL_FreeSurface(imageSurface);
SDL_Rect imageRect = {0, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT};

int switchAnimation = 0;

SDL_Event e;
int quit = 0;
while (!quit) {
    while (SDL_PollEvent(&e) != 0) {
        if (e.type == SDL_QUIT) {
            quit = 1;
        }
    }


                                  //thread for synchronizing between audio and animation
                        SDL_Thread* thread = SDL_CreateThread(audioThread, "Audio", NULL);

                      
                        // struct to hold the position and size of the sprite
                            SDL_Rect dest;

                       // get the dimensions of texture
                            SDL_QueryTexture(imageTexture, NULL, NULL, &dest.w, &dest.h);

                       // position the sprite at the bottom center of the window
                       // origin is the top left corner, positive y is down
                           dest.x = (800 - dest.w) / 2;

                      // require float resolution for y position
                           float y_pos = 600;

                       // animation loop
                           while (dest.y >= -dest.h)


                            {
                 // clear the window
                 SDL_RenderClear(renderer);

                 // set the y position in the struct
                 dest.y = (int) y_pos;

                 // draw the image to the window
                 SDL_RenderCopy(renderer, imageTexture, NULL, &dest);
                 SDL_RenderPresent(rend);

                // update sprite position
                 y_pos -= (float) 300 / 60;
                 SDL_Delay(1000/30);

                 }


                 int threadReturnValue;
                 SDL_WaitThread(thread, &threadReturnValue);

                // Clear the renderer
                SDL_RenderClear(renderer);

               // Render the text texture
                SDL_RenderCopy(rend, tex, NULL, NULL);
                SDL_RenderPresent(renderer);

                SDL_FreeSuraface(imageSurface);
                SDL_DestroyTexture(imageTexture);


        

       
        SDL_RenderClear(renderer);
    if (TTF_Init() == -1) {
        printf("SDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError());
        cleanup();
        return -1;
     }

   font = TTF_OpenFont("ChrustyRock-ORLA.ttf", 28); // Ajustez la taille de la police au besoin
    if (!font) {
        printf("Failed to load font! SDL_ttf Error: %s\n", TTF_GetError());
        cleanup();
        return -1;
    }

    // Afficher le menu
        SDL_Color textColor = {255, 255, 255};
        SDL_Surface* textSurface = TTF_RenderText_Solid(font, "Menu Principal", textColor);
        SDL_Texture* textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);

        SDL_Rect textRect = {10, 10, textSurface->w, textSurface->h};
        SDL_RenderCopy(renderer, textTexture, NULL, &textRect);


        // Mettre à jour le rendu
        SDL_RenderPresent(renderer);

    // Effacer le rendu
    SDL_RenderClear(renderer);

    // Afficher l'image
    SDL_RenderCopy(renderer, imageTexture, NULL, &imageRect);

    // Mettre à jour le rendu
    SDL_RenderPresent(renderer);

    // Libérer la surface et la texture associée
        SDL_FreeSurface(textSurface);
        SDL_DestroyTexture(textTexture);


}
cleanup();

return 0;

}
`

You’re doing a lot of SDL_RenderClear and SDL_RenderPresent, which is usually not preferred.
The only time it’s really necessary/needed is when using multiple render targets, for example if you want to clear a render target texture to a specific color before rendering to it, and then finally render that render target texture to the window.

In your case, you only need to call SDL_RenderClear and SDL_RenderPresent once per game loop.
A typical render loop usually consist of SDL_RenderClear being called first, then all the graphical objects are rendered and then finally SDL_RenderPresent are called to update the window and show the rendered content in the window.

Besides the issue with the rendering, you’re also creating and loading assets during runtime, which also usually isn’t preferred. The usual pattern is that assets are created/initialized/loaded at the start of the application/game and destroyed/deinitialized/unloaded when the application/game is shutdown.
Stuff that needs to be created dynamically, for example text, is ok to be created during runtime, as long as the asset(s) is destroyed after the asset isn’t needed anymore.

Below I’ve added a fix-up of your code, which shows how an application/game is usually set up.
I also added a function for easy rendering of text to the window.

I honestly didn’t understand what you wanted to do with your sprite, if you wanted to move it in some specific way etc, so I just made it move between the left and right side of the window. Check the update function to see how that’s done.

Code fix-up
#include <stdio.h>
#include <string>

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#include <SDL2/SDL_ttf.h>

const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
const int SCROLL_SPEED = 2;
const int ANIMATION_DURATION = 1000 / 30; // Durée de l’animation en millisecondes (5 secondes)

SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;

SDL_Texture* imageTexture = nullptr;
SDL_Rect spriteRect = {0, 0, 0, 0};

Mix_Music* music = nullptr;

TTF_Font* menuFont = nullptr;

double oldTime = 0.0;
double newTime = 0.0;
double deltaTime = 0.0;

// 1 = moving to the right, 0 = moving to the left
int spriteXDirection = 1;

float spriteXPosition = 0.0f;
float spriteYPosition = 0.0f;
float spriteMovementSpeed = 300.0f;

bool running = true;

bool initSDL()
{
	if(SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		printf("Error: SDL could not initialize! %s\n", SDL_GetError());

		return false;
	}

	if(IMG_Init(IMG_InitFlags::IMG_INIT_PNG) != IMG_InitFlags::IMG_INIT_PNG)
	{
		printf("Error: SDL_Image could not initialize. %s\n", IMG_GetError());
		return false;
	}

	if(TTF_Init() == -1)
	{
		printf("Error: SDL_TTF could not initialize! %s\n", TTF_GetError());
		return false;
	}

	if (Mix_Init(MIX_InitFlags::MIX_INIT_MP3) != MIX_InitFlags::MIX_INIT_MP3)
	{
		printf("Error: SDL_Mixer could not initialize! %s\n", Mix_GetError());
		return false;
	}

	window = SDL_CreateWindow("SDL Animation", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
	if(!window)
	{
		printf("Error: SDL window could not be created! %s\n", SDL_GetError());
		return false;
	}

	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
	if(!renderer)
	{
		printf("Error: SDL renderer could not be created! %s\n", SDL_GetError());
		return false;
	}

	SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
	SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

	SDL_RenderClear(renderer);
	SDL_RenderPresent(renderer);

	return true;
}

void cleanupSDL()
{
	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);

	Mix_Quit();
	TTF_Quit();
	IMG_Quit();
	SDL_Quit();
}

bool initAssets()
{
	SDL_Surface* imageSurface = IMG_Load("hello.png");
	if(!imageSurface)
	{
		printf("Error: failed to load image! %s\n", SDL_GetError());
		return false;
	}
	imageTexture = SDL_CreateTextureFromSurface(renderer, imageSurface);

	SDL_FreeSurface(imageSurface);

	if(!imageTexture)
	{
		printf("Error: failed to create SDL texture! %s\n", SDL_GetError());
		return false;
	}

	int textureWidth = 0;
	int textureHeight = 0;
	SDL_QueryTexture(imageTexture, nullptr, nullptr, &textureWidth, &textureHeight);

	// Position the sprite at the bottom center of the window
	// Origin is the top left corner of the sprite, positive y is down
	spriteXPosition = (float)((WINDOW_WIDTH * 0.5f) - (textureWidth * 0.5f));
	spriteYPosition = (float)(WINDOW_HEIGHT - textureHeight);

	spriteRect.x = (int)spriteXPosition;
	spriteRect.y = (int)spriteYPosition;
	spriteRect.w = textureWidth;
	spriteRect.h = textureHeight;

	// Ajustez la taille de la police au besoin
	menuFont = TTF_OpenFont("ChrustyRock-ORLA.ttf", 28); 
	if(!menuFont)
	{
		printf("Error: failed to load font! %s\n", TTF_GetError());
		return false;
	}

	return true;
}

void cleanupAssets()
{
	TTF_CloseFont(menuFont);
	menuFont = nullptr;

	SDL_DestroyTexture(imageTexture);
	imageTexture = nullptr;
}

bool renderText(TTF_Font* font, const std::string& text, const SDL_Point position, const SDL_Color color)
{
	SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str(), color);

	if(!surface)
		return false;

	SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);

	SDL_FreeSurface(surface);
	surface = nullptr;

	if(!texture)
		return false;

	int textureWidth = 0;
	int textureHeight = 0;
	SDL_QueryTexture(texture, nullptr, nullptr, &textureWidth, &textureHeight);

	const SDL_Rect dstRect = {position.x, position.y, textureWidth, textureHeight};

	SDL_RenderCopy(renderer, texture, nullptr, &dstRect);

	SDL_DestroyTexture(texture);
	texture = nullptr;

	return true;
}

void update()
{
	if(spriteXDirection == 1)
	{
		spriteXPosition += spriteMovementSpeed * (float)deltaTime;

		if(spriteXPosition >= (float)(WINDOW_WIDTH - spriteRect.w))
			spriteXDirection = 0;
	}

	else
	{
		spriteXPosition -= spriteMovementSpeed * (float)deltaTime;

		if(spriteXPosition <= 0.0f)
			spriteXDirection = 1;
	}

	spriteRect.x = (int)spriteXPosition;
	spriteRect.y = (int)spriteYPosition;
}

void render()
{
	// Clear the current render target, with the specified clear color (white in this case)
	SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
	SDL_RenderClear(renderer);

	//////////////////////////////////////////////////////////////////////////

	// Render the graphical objects here

	/*
	* Render some example text to the window
	* The arguments for renderText function are as follows:
	* The font to use for the text rendering
	* The text to render
	* The color of the text
	*/
	renderText(menuFont, "Menu Principal",								{10, 10}, {0,	0, 0, 255});
	renderText(menuFont, "Some example text",							{10, 40}, {255, 0, 0, 255});
	renderText(menuFont, "Some example text printed with transparency", {10, 70}, {255, 0, 0, 80});

	// Render the sprite to the window
	SDL_RenderCopy(renderer, imageTexture, nullptr, &spriteRect);



	//////////////////////////////////////////////////////////////////////////

	// Update the window
	SDL_RenderPresent(renderer);
}

int audioThread(void* data)
{
	Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048);

	Mix_Music* music = Mix_LoadMUS("initjeuaudio.mp3");

	if(music)
		Mix_PlayMusic(music, 0); // -1 for looping, 0 for playing once

	while(Mix_PlayingMusic())
	{
		SDL_Delay(1000 / 60);
	}

	if(music)
		Mix_FreeMusic(music);

	Mix_CloseAudio();
	Mix_Quit();

	return 0;
}

int main(int argc, char** argv)
{
	if(!initSDL())
		running = false;

	if(!initAssets())
		running = false;

	// Thread for synchronizing between audio and animation
	SDL_Thread* thread = SDL_CreateThread(audioThread, "Audio", NULL);

	SDL_Event event;
	while(running)
	{
		while(SDL_PollEvent(&event))
		{
			switch(event.type)
			{
				case SDL_QUIT:
				{
					running = false;

					break;
				}

				default:
					break;
			}
		}

		newTime = (double)SDL_GetTicks();
		deltaTime = (newTime - oldTime) * 0.001;
		oldTime = newTime;

		update();
		render();

		// Let the processor do something else for a short while
		SDL_Delay(1);
	}

	int threadReturnValue = 0;
	SDL_WaitThread(thread, &threadReturnValue);

	cleanupAssets();
	cleanupSDL();

	return 0;
}

it is perfectly done thank you so much , but what i just wanted is that once i repeat the animation of the image one time with that sound audio , it disappears from the window SDL so i can start a new animation where i make it as menu , do you know how to make it , thank you again :heart: :smiling_face_with_three_hearts:and im so sorry for borthering you :pensive: its just that im beginner in SDL and its a project that my teacher gave it to me .

Sorry, I don’t think I can help out more. It seems to be a bit of a language barrier here so I’m sadly having a hard time understanding what you mean and what you want to do.

I want to suggest you to check out this SDL tutorial site:

I learned a lot from that site when I was new to programming and SDL.

Since it’s a school assignment, I think it’s best that you consult with your teacher about what you want to do.

You, amaris, seems to be in the mindset of doing one thing, and when that’s done you do another thing, and so on. This might work OK if you only have one thing happening at each point in time but in games you usually have many things going on so you cannot do everything all at once.

In a game you normally have a “game loop”, something like:

while game is running
    handle input
    update game logic
    draw graphics

To keep the game running you cannot allow any of these steps to block. For example, if you detect that the player is pressing the space bar to jump you cannot enter a loop that executes all the steps of the jump because that would ignore everything else that is going on in the game (updating enemies, collision detection, other key presses, etc.) Instead what you could do is to set some variable(s) that affects how the “update” step is executed.

// when detecting a jump button press
if (!player.is_jumping)
{
	player.is_jumping = true;
	player.velcoity.y = 10.0; // initial jump velocity
}
// when updating the player
player.position.x += player.velcoity.x;
player.position.y += player.velcoity.y;
if (collides_with_ground(player))
{
	player.is_jumping = false; // allow jumps again
	player.velcoity.y = 0;
}
else
{
	player.velcoity.y -= 0.8; // decrease velocity (simulates gravity)
}

The above is just a simplified and incomplete example to give you the general idea. The exact details would depend on the game.

It’s a similar situation with the animations. You cannot run the whole animation all at once. You need to do it in steps. So whatever it is that triggers the animation (it might be the user pressing a button, the player colliding with something, or a combination) the first thing you would do is update/add some “animation state” and then it would be the job of the animation update code to update it each frame.

To do something like this you need to think about how to represent an animation using data. In one of my games, an animation is essentially just a list of SDL_Rect objects that is applied to a “sprite” object. Each rect represents one frame of the animation and specifies the graphics area to show on a “sprite sheet”. Another alternative if you don’t use a “sprite sheet” would be to essentially let an animation be a list of textures (where one texture represent one frame of the animation).

After writing this and after taking a closer look at your code again I realize this was perhaps not what you meant by “animation”. I was thinking about changing the appearance of the sprite while your code seems to be more concerned with moving the sprite but i think what I said here is still relevant.

You might want to look up some tutorials about “game loops” and “game states” to get some ideas about how to structure your code.

Another advantage of trying to represent as much of the game state as possible using data structures is that it becomes easier if you later want to be able to save and restore the state of the program.