How to render image when special key event is pressed?

Hello, I’m writing something like oldschool Frogger game. My problem is, how to render some textures when specific key is pressed?
For example, let’s assume I want to pause game, so I press ‘p’, and the game will go on only when I press ‘p’ again. So, my question is, how to render a “PAUSED” image while the game is paused, but only then?
My code:
`case SDLK_p:

						paused = true;
						while (paused) {
							while (SDL_PollEvent(&pauseEvent)) {
								if (pauseEvent.type == SDL_QUIT) {
									quit = true;
									paused = false;
									continue;
								}
								else if (pauseEvent.type == SDL_KEYDOWN) {
									if (pauseEvent.key.keysym.sym == SDLK_p) {
										paused = false;
									}
								}
							}
						}
					break;`

And this is what I want to render:

SDL_Rect pausedPos = { 150, MAP_HEIGHT / 2, 400, 230 };
SDL_RenderSetViewport(gRenderer, &pausedPos);
SDL_RenderCopy(gRenderer, gamePaused, NULL, NULL);
SDL_Rect undoView = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
SDL_RenderSetViewport(gRenderer, &undoView);

I would appreciate any help or ideas, I’m stuck on this for a while… Thanks.

Hello there and welcome to the forum!

Since you’re already handling the paused state in your code, with the bool variable paused, I would use that bool to check whether the paused texture should be rendered or not.

Example code:

if(paused)
{
	const SDL_Rect pausedPos	= {150, MAP_HEIGHT / 2, 400,			230};
	const SDL_Rect undoView		= {0,	0,				SCREEN_WIDTH,	SCREEN_HEIGHT};

	SDL_RenderSetViewport(gRenderer, &pausedPos);

	SDL_RenderCopy(gRenderer, gamePaused, NULL, NULL);

	SDL_RenderSetViewport(gRenderer, &undoView);
}

Hello Naith and thank you for your reply.
I tried doing as you recommended, just a little modification:

case SDLK_p:
						paused = true;
						while (paused) {
							if (paused)
							{
								const SDL_Rect pausedPos = { 150, MAP_HEIGHT / 2, 400,	230 };
								const SDL_Rect undoView = { 0,	0,	SCREEN_WIDTH,	SCREEN_HEIGHT };
								SDL_RenderSetViewport(gRenderer, &pausedPos);
								SDL_RenderCopy(gRenderer, gamePaused, NULL, NULL);
								SDL_RenderSetViewport(gRenderer, &undoView);
							}
							while (SDL_PollEvent(&pauseEvent)) {
								if (pauseEvent.type == SDL_QUIT) {
									quit = true;
									paused = false;
									continue;
								}
								else if (pauseEvent.type == SDL_KEYDOWN) {
									if (pauseEvent.key.keysym.sym == SDLK_p) {
										paused = false;
									}
								}
							}
						}
						break;

This is not the only way I tried it, but every time the same problem occurs - after pressing ‘p’ key game stops, paused image is not displayed, and when I press ‘p’ again to go back to the game, it doesn’t work - again, nothing happens, it seems like it’s lagged or something… I don’t know where is the problem. Maybe you know where is the source of this behaviour?

Without this if() part with render stuff ‘p’ key works as it should - game stops and begins on press.
Thanks again for any answers

Little EDIT:
I moved this if() inside while (SDL_PollEvent(&pauseEvent), there is this little improvement that ‘p’ key works, but nothing more is displayed…

You usually don’t want to render objects (by using SDL_RenderCopy*() etc) inside the event loop. The reason for this is, whenever the code leaves the event loop (i.e, when the event queue is empty), whatever is rendered while the loop executes is no longer rendered. In this case, this might be what you want with your paused texture, but it’s always better to separate the game update/logic and the game rendering.

Example code on how the separation should usually be handled:

View code
void Update(void)
{
	while(SDL_PollEvent(&Event))
	{
		switch(Event.type)
		{
			case SDL_QUIT:
			{
				quit = true;

				break;
			}

			case SDL_KEYDOWN:
			{
				switch(Event.key.keysym.sym)
				{
					case SDLK_p:
					{
						// Just a fancy way of doing:
						// If paused == true,  paused is set to false
						// If paused == false, paused is set to true
						paused = !paused;

						break;
					}

					default:
						break;
				}

				break;
			}

			default:
				break;
		}
	}
}

void Render(void)
{
	if(paused)
	{
		const SDL_Rect pausedPos	= {150, MAP_HEIGHT / 2, 400,			230};
		const SDL_Rect undoView		= {0,	0,				SCREEN_WIDTH,	SCREEN_HEIGHT};

		SDL_RenderSetViewport(gRenderer, &pausedPos);
		SDL_RenderCopy(gRenderer, gamePaused, NULL, NULL);
		SDL_RenderSetViewport(gRenderer, &undoView);
	}
}

int main(int argc, char* argv[])
{
	// Create the SDL window, the SDL renderer, create texture(s) etc
	Create();

	// Mainloop
	while(!quit)
	{
		Update();
		Render();
	}

	// Destroy the SDL window, the SDL renderer, the texture(s) etc
	Destroy();

	return 0;
}

The reason nothing else is displayed is because when the P key is pressed, your application busy-waits for the P key to be pressed again, and the display is never updated until it’s unpaused.

A better way is to set a flag and decide what to do based on that elsewhere, as @Naith showed.

Hello again and thanks for your answers. I tried doing as you said @Naith, now in my event loop when it’s about ‘p’ key it looks just like this:

    case SDLK_p: 
      paused = !paused;
      break;

btw, a really nice way of doing this with ! negation, thanks :slight_smile:

I have a big Render() function which generates everything in this game,
so I added this “if” there:

if (play)
			{
				SDL_SetRenderDrawColor(gRenderer, 2, 100, 64, 1);
				SDL_RenderClear(gRenderer);
				SDL_RenderCopy(gRenderer, plansza, NULL, &otherRect[2]);
				renderMovingObjects(enemiesRect, alliesRect);
				SDL_RenderCopy(gRenderer, frog, NULL, &otherRect[0]);
				renderWonPosition();
				renderHearts(lives);

				if (paused) {
					SDL_Rect pausedPos = { 150, MAP_HEIGHT / 2, 400, 230 };
					SDL_RenderSetViewport(gRenderer, &pausedPos);
					SDL_RenderCopy(gRenderer, gamePaused, NULL, NULL);
					SDL_Rect undoView = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
					SDL_RenderSetViewport(gRenderer, &undoView);
					}

				checkIfWon(&otherRect[0], &lives, &play, &places);
				SDL_RenderPresent(gRenderer);
			}

And this is half-working : when I press the ‘p’ key the image is displayed, and is un-displayed when I press ‘p’ key again. But how to make the game stop at this point? I tried adding inside my Render() function another while (SDL_PollEvent(&pauseEvent)) but it seems I really don’t know how to connect this image rendering with waiting for ‘p’ to be pressed again. :confused:

You usually only need 1 event loop (executing SDL_PollEvent()) so you shouldn’t need to create another one in your render function.
Like I wrote in my previous post: update/logic and rendering should be separated, so event polling shouldn’t be handled in the render function either way.

When it comes to pausing in games, when a game is in a paused state, in the end it all comes to not updating the game and its game objects. Event polling still need to be handled though, so that the player can unpause/resume the game by pressing, for example, the P key on the keyboard. But when it comes to other input, like player movement code, like the arrow keys for example, clicking with the mouse on HUD button(s) on the screen etc should be temporarially disabled.

Below I’ve added another example code on how to handle a paused state in a game. What the code basically does is leaving the update function earlier, if IsPaused is true (i.e, the game is paused). If not, the rest of the code in Update() is executed, effectively updating the game and its game objects.

Also note that I’ve added another way of handling input from the keyboard. Instead of using the event type SDL_KEYDOWN, I’ve used the SDL_GetKeyboardState() function, to retrieve the current state of all the keys on the keyboard.
After the state is retrieved, the new function KeyPressed() can be used to check against a specific key on the keyboard, and then act accordingly to that result.

View code example
// Variables

bool Running	= false;
bool IsPaused	= false;

bool Keys[SDL_NUM_SCANCODES];
memset(Keys, false, sizeof(Keys));

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

// Function declarations

void HandleEvents	(void);
void Update			(void);
void Render			(void);
bool KeyPressed		(const SDL_Scancode Scancode);

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

int main(int argc, char *argv[])
{
	Create();

	while(Running)
	{
		Update();
		Render();
	}

	Destroy();

	return 0;
}

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

// Function bodies

void HandleEvents(void)
{
	while(SDL_PollEvent(&Event))
	{
		switch(Event.type)
		{
			case SDL_QUIT:
			{
				Running = false;

				break;
			}

			default:
				break;
		}
	}

	// Get the current keyboard state and save it into the keys array
	memcpy(Keys, SDL_GetKeyboardState(nullptr), SDL_NUM_SCANCODES * sizeof(bool));
}

void Update(void)
{
	HandleEvents();

	if(KeyPressed(SDL_SCANCODE_P))
	{
		IsPaused = !IsPaused;

		if(IsPaused)
			Musicplayer.Pause();

		else
			MusicPlayer.Resume();
	}

	if(IsPaused)
		return;

	// Update all the game objects
	CurrentMap.Update();
	Player.Update();
	Enemy.Update();
}

void Render(void)
{
	SDL_SetRenderDrawColor(pRenderer, 255, 255, 255, 255);
	SDL_RenderClear(pRenderer);

	// Render all the game objects here
	CurrentMap.Render();
	Player.Render();
	Enemy.Render();

	if(IsPaused)
	{
		const SDL_Rect AlphaRect = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};

		const SDL_Rect PauseTexPos = {	(SCREEN_WIDTH	/ 2) - (PauseTexWidth	/ 2),
										(SCREEN_HEIGHT	/ 2) - (PauseTexHeight	/ 2),
										(PauseTexWidth),
										(PauseTexHeight)};

		// Render a black, half-transparent, background to cover the game world and its game objects
		SDL_SetRenderDrawColor(pRenderer, 0, 0, 0, 128);
		SDL_RenderFillRect(pRenderer, &AlphaRect);

		// And render the pause texture in the middle of the window
		SDL_RenderCopy(pRenderer, PauseTex, NULL, &PauseTexPos);
	}

	SDL_RenderPresent(pRenderer);
}

bool KeyPressed(const SDL_Scancode Scancode)
{
	return Keys[Scancode];
}