Rendering rectangles with SDL2 : Over rendering

Hello everyone !

I’m currently learning how to create a basic window and rectangles on screen with SDL2 in C++. Here are the main and Game files :

main.cpp

#include <iostream>
#include "Game.hpp"
#include "Player.hpp"
#include "Obstacle.hpp"

int main(int argc, char **argv)
{
	Game *game = new Game();
	
	if(game->init() != true)
	{
		std::cerr << "Error during initialization..." << std::endl;
	}
	else
	{
		Player *player = new Player(400,550,40,40);
		Obstacle *obstacle = new Obstacle();
		
		SDL_Rect player_box;
		SDL_Rect obstacle_box;
		
		while(game->handleEvents(*player) == true)
		{
			player_box.x = player->get_x();
			player_box.y = player->get_y();
			player_box.w = player->get_w();
			player_box.h = player->get_h();
			
			obstacle_box.x = obstacle->get_x();
			obstacle_box.y = obstacle->get_y();
			obstacle_box.w = obstacle->get_w();
			obstacle_box.h = obstacle->get_h();
			
			game->draw(player_box);
			game->draw(obstacle_box);
			
			if(obstacle->get_y() <= 600)
			{
				obstacle->move(1);
			}
			else
			{
				obstacle->set_y(20);
			}
			
			game->update();
		}
	}

	delete game;
	
	return 0;
}

Game.cpp

	#include <iostream>
#include <SDL2/SDL.h>
#include "Game.hpp"
#include "Player.hpp"

Game::Game()
{
	
}

Game::~Game()
{
	SDL_DestroyWindow(window);
	SDL_Quit();
}

bool Game::init()
{
	bool success = true;
	
	if(SDL_Init(SDL_INIT_VIDEO) != 0)
	{
		std::cerr << "Error : Video init " << SDL_GetError() << std::endl;
		success = false;
	}
	else
	{
		window = SDL_CreateWindow("C++",
									SDL_WINDOWPOS_CENTERED,
									SDL_WINDOWPOS_CENTERED,
									800,
									600,
									SDL_WINDOW_SHOWN);
		if(window == NULL)
		{
			std::cerr << "Error : Window init " << SDL_GetError() << std::endl;
			success = false;
		}
		else
		{
			window_renderer = SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
			
			if(window_renderer == NULL)
			{
				std::cerr << "Error : Video init " << SDL_GetError() << std::endl;
				success = false;
			}
		}
	}
	
	return success;
}

bool Game::handleEvents(Player &player)
{
	bool isRunning = true;
	SDL_Event ev;
	
	while(SDL_PollEvent(&ev) != 0)
	{
		if(ev.type == SDL_QUIT)
		{
			isRunning = false;
		}
		if(ev.type == SDL_KEYDOWN)
		{
			switch(ev.key.keysym.sym)
			{
				case SDLK_ESCAPE:
					isRunning = false;
					break;
					
				case SDLK_LEFT:
					if(player.get_x() >= 20)
					{
						player.move(-10);
					}
					break;
					
				case SDLK_RIGHT:
					if(player.get_x() <= 750)
					{
						player.move(10);
					}
					break;
					
				default:
					break;
			}
		}
	}
	
	return isRunning;
}

void Game::draw(SDL_Rect &rect)
{
	SDL_RenderDrawRect(window_renderer,&rect);
	SDL_SetRenderDrawColor(window_renderer,255,255,255,0);
	SDL_RenderFillRect(window_renderer, &rect);
	SDL_RenderPresent(window_renderer);
}

void Game::update()
{
	SDL_Delay(1000/60);
	SDL_SetRenderDrawColor(window_renderer,0,0,0,0);
	SDL_RenderClear(window_renderer);
	SDL_RenderPresent(window_renderer);
}

I would like to draw two rectangles, the player and the obstacle via the Game::draw() function. But the problem is that each time I update the screen, It goes black/white over and over. How can I solve this ? Maybe using SDL_Surface instead of drawing with the renderer ?

Kind Regards

Hey there, I will try and help you solve the issue.

There are a couple of issues or problems with the draw and update functions, rather then try to figure out what your trying to achieve I will skip over that and try explain how to draw two different rectangles of different color.

What you need to do is

  • Set the Renderer Color
  • Clear the Renderer
  • Set the Renderer Color again “Color for the player”
  • Draw the Player
  • Set the Renderer Color again “Color for the object”
  • Draw the Object
  • Renderer Present

Here is a simple example, “My code doesn’t do error checking etc, its just an example”

int main(int argc, char **argv) {
    	bool quit = false;

    	SDL_Init(SDL_INIT_VIDEO);
    	SDL_Window *window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
    	SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

    	SDL_Rect player{ 20, 20, 50, 50 };
    	SDL_Rect object{ 100, 100, 50, 50 };

    	while (!quit) {
    		SDL_Event e;
    		while (SDL_PollEvent(&e) != 0) {
    			if (e.type == SDL_QUIT) {
    				quit = true;
    			}
    			if (e.type == SDL_KEYDOWN) {
    				switch (e.key.keysym.sym) {
    				case SDLK_ESCAPE:
    					quit = true;
    					break;
    				case SDLK_LEFT:
    					if (player.x >= 0) {
    						player.x -= 10;
    					}
    					break;
    				case SDLK_RIGHT:
    					if (player.x <= 800) {
    						player.x += 10;
    					}
    					break;
    				}
    			}
    		}

    		// Set Color to White
    		SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
    		// Clear the screen 
    		SDL_RenderClear(renderer);
    		// Set Color to Blue
    		SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
    		// Draw filled Rectangle Player, the color is blue as we set this above ^
    		SDL_RenderFillRect(renderer, &player);
    		// Set Color to Red
    		SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    		// Draw filled Rectangle Object, the color is red as we set this above ^
    		SDL_RenderFillRect(renderer, &object);

    		// Update our screen
    		SDL_RenderPresent(renderer);
    	}
    	// Clean up
    	SDL_DestroyRenderer(renderer);
    	SDL_DestroyWindow(window);
    	SDL_Quit();

    	return 0;
    }

Please message is you have any questions I would be more then happy to help you out.

Hello Smiles !
Thanks for your answer.

Well, the fact is that I did understand that, everytime I want to draw something on the screen, I have to use SDL_SetRenderDrawColor in order to set the color and then SDL_RenderFillRect in order to fill the selected rectangle with the selected renderer (with the colour set).

But, in my code, I consider the two SDL_Rect (both player and obstacle) as being white. When I execute the program, it goes black/white very fast because the program is updated and every time I update the screen I use :

void Game::draw(SDL_Rect &rect)
{
	SDL_SetRenderDrawColor(window_renderer,255,255,255,255);
	SDL_RenderFillRect(window_renderer, &rect);
	SDL_RenderPresent(window_renderer);
}

void Game::update()
{
	SDL_SetRenderDrawColor(window_renderer,0,0,0,255);
	SDL_RenderClear(window_renderer);
	SDL_RenderPresent(window_renderer);
}

I may have not understood, but I did solve (in some ways) the problem by changing the update function in :

    void Game::update()
{
	SDL_RenderPresent(window_renderer);
}

But this time, the rectangles moves aren’t deleted so they progressively fill the screen because I don’t render the screen again in black color.

Your logic is a bit messy regarding drawing / updating.

This should fix your issue, but I don’t recommend it.
Have a look at the approach and layout that I took above with my example.
If you need an example game loop broken up into multiple classes let me know.

while(game->handleEvents(*player) == true) {
         // Set background color to black
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        // Clear the screen 
    	SDL_RenderClear(renderer);
        ...
        game->draw(player_box);
        game->draw(obstacle_box);
        ...
        game->update();
    }

    void Game::draw(SDL_Rect &rect) {
        // Set background color to white
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
        SDL_RenderFillRect(window_renderer, &rect);
    }

    void Game::update() {
        // Draws everything you rendered to the screen.
        SDL_RenderPresent(window_renderer);
    }