In this mini-project we will get to use C++ condition checking with some if statements. Like the last project we will draw some shapes (slightly different this time) and move them. This time however instead of letting them disappear off of the screen we will detect when they are leaving the screen on any of the four sides and then “bounce” them back the other way. 

[widgets_on_pages id=”udemy_advert_cpp_1″][widgets_on_pages id=”udemy_code_details”]

About this project

Skill level 1
Time to complete 30 minutes

New concepts

  • Using C++ conditions
  • Using C++ if statements
  • Another look at the SFML Shape class
  • Introduction to C++ constants
  • The most basic collision detection in the world

To get started we need to create a new project, complete with all the required properties to work with SFML. If you haven’t completed the Building your first SFML game project you will need to do that first. In that project we created a Visual Studio project template, so that starting a new SFML project is now a two minute job instead of a half an hour long chore.

The next steps set up our new SFML C++ project. If you want more detailed steps and images to perform these steps check the previous project. Game variables demo: Moving circles.

  1. Open Visual Studio and from the main menu choose File | New Project. In the left-hand menu click C++. Select the HelloSFML template and name your project Conditions And Branching.
  2. Now click OK.
  3. Right-click the HelloSFML.cpp file under the Source Files heading from the right-hand Solution Explorer window. Choose Rename and rename the file to Main. This is a more appropriate name as this will indeed be our “main” source file.
  4. Open Main.cpp by double-clicking it. Now delete all of its contents as we will be starting fresh and discussing all of the code as well.
  5. Copy & paste the SFML .dll files in the YOUR_DRIVE:\SFML\bin to YOUR_DRIVE:\Visual Studio Stuff\Projects\Conditions And Branching\Conditions And Branching.

Now we can get coding.

[widgets_on_pages id=”bcgp_cfgp_gpp”]

Writing the code

Copy and paste all the code that follows into your currently empty, Main.cpp file. You don’t need to type it all in order to learn, but you should definitely read all the comments and analyse as best you can the code before we go through it a bit at a time.

// These "include" code from the C++ library and SFML too
#include "stdafx.h"
#include <SFML/Graphics.hpp>

// This is where our game starts from
int main()
{
	// Make a window that is 1000 by 700 pixels
	// And has the title "Conditions and branching demo"
	// Change the resolution if it is unsuitable for you

	int windowWidth = 1000;
	int windowHeight = 700;

	sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "Conditions and branching demo");

	// Let's make a few shapes
	// define a circle with radius = 100
	// Our new circle is called "tri"!!??!!
	sf::CircleShape tri(100);

	// A circle with 3 points
	// There is a clue in the variable name
	tri.setPointCount(3);

	// Here is another "circle"
	sf::CircleShape bumpy(100);
	bumpy.setPointCount(15);

	// Now lets have some variables for their positions (x and y)
	float triX = 0;
	float triY = 0;

	float bumpyX = windowWidth;
	float bumpyY = windowHeight;

	// Here are some variables that can never change.
	// Thet are called constants.
	// We make their names all uppercase to remind us.
	// We use the const keyword to make them constant.
	// We will see their purpose soon.
	const float LEFT = -1;
	const float RIGHT = 1;
	const float UP = -1;
	const float DOWN = 1;

	// The direction is not constant
	// tri starts with RIGHT and DOWN
	float triDirectionX = RIGHT;
	float triDirectionY = DOWN;

	// The direction is not constant
	// bumpy starts with LEFT and UP
	float bumpyDirectionX = LEFT;
	float bumpyDirectionY = UP;

	// How fast will they move
	float speed = .1;

	// This "while" loop goes round and round- perhaps forever
	// It only stops when the player closes the window
	while (window.isOpen())
	{

		// Has the player closed the game window?
		sf::Event event;
		while (window.pollEvent(event))
		{
			if (event.type == sf::Event::Closed)
				// Someone closed the window- bye
				window.close();
		}

		// Handle the ai, physics and process updates
		// Watch out for collisions with the edge of the screen

		// First the tri shape
		if (triX > windowWidth) {
			// Triangle is near right of screen
			triDirectionX = LEFT;
		}
		else if (triX < 0) {
			// Triangle is near left of screen
			triDirectionX = RIGHT;
		}

		if (triY > windowHeight) {
			// Triangle is near bottom of screen
			triDirectionY = UP;
		}
		else if (triY < 0) {
			// Triangle is near top of screen
			triDirectionY = DOWN;
		}

		// Now for bumpy
		if (bumpyX > windowWidth) {
			// Bumpy shape is near right of screen
			bumpyDirectionX = LEFT;
		}
		else if (bumpyX < 0) {
			// Bumpy shape is near left of screen
			bumpyDirectionX = RIGHT;
		}

		if (bumpyY > windowHeight) {
			// Bumpy shape is near bottom of screen
			bumpyDirectionY = UP;
		}
		else if (bumpyY < 0) {
			// Bumpy shape is near top of screen
			bumpyDirectionY = DOWN;
		}

		// Let's move our two shapes based of the values
		// held by the appropriate ...directionX and ...directionY
		// and the speed variable
		triX = triX + speed * triDirectionX;
		triY = triY + speed * triDirectionY;

		bumpyX = bumpyX + speed * bumpyDirectionX;
		bumpyY = bumpyY + speed * bumpyDirectionY;

		// Set the position of the shapes
		tri.setPosition(triX, triY);
		bumpy.setPosition(bumpyX, bumpyY);

		// Clear everything from the last run of the while loop
		window.clear();

		// Draw our game scene
		window.draw(tri);
		window.draw(bumpy);

		// Show everything we just drew
		window.display();
	}// This is the end of the "while" loop

	return 0;
}

Run the demo by clicking the Local Windows Debugger button.


sfml_moving_bouncing_shapes_demo_debugging

Try changing the value of speed by a significant amount, perhaps to .5 and watch how the shapes dramatically speed up.

Now we can analyse the code a bit at a time.

Analyzing the code

[widgets_on_pages id=”udemy_advert_cpp_2″][widgets_on_pages id=”udemy_code_details”]
The first thing we see in the code we have seen before. Two #include directives to make available the necessary files to make a basic Windows program and the SFML graphics module. Then we enter the main function and our code will begin to be executed.

// These "include" code from the C++ library and SFML too
#include "stdafx.h"
#include <SFML/Graphics.hpp>

// This is where our game starts from
int main()
{

First, we make a window using an SFML RenderWindow class. This we do very slightly differently to how we did it before. Notice we declare two variables windowWidth and windowHeight. We initialize them to 1000 and 700 respectively. We then use these two variables in the line of code that creates a window. This, of course, will have the effect of creating a window for our game that is 1000 pixels wide and 700 pixels high.

We will see what advantage having these two variables will bring us, soon.

// Make a window that is 1000 by 700 pixels
// And has the title "Conditions and branching demo"
// Change the resolution if it is unsuitable for you

int windowWidth = 1000;
int windowHeight = 700;

sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "Conditions and branching demo");

Next, we make some shapes using the SFML CircleShape class. First, slightly counter-intuitively we make a circle called tri? The next line of code sets the number of points using the setPointCount function, to 3. This is how we can make a whole variety of different shapes using SFML. Simply set the number of points and you have the shape you require. By setting three points we get a triangle.

Next, we create another CircleShape object and call it bumpy. We set the number of points at  15. If you ran the code you will have seen you get something much more like a circle, albeit quite a bumpy circle, hence the name.

Lastly, in this block of code, we declare and initialize some variables to hold the horizontal and vertical locations of our two shapes; tri and bumpy. We name the variables appropriately as  triX, triY, bumpyX and bumpyY. What is perhaps more interesting is the values to which we initialize them.

The triX and triY variables are both set to zero and so when used to position the tri shape will be in the top left corner of the screen. We initialize the bumpyX and bumpyY variables with windowWidth and windowHeight respectively. As windowWidth and windowHeight, as you would expect, contain the width and height of our window; when these variables are to set the position of the bumpy shape, it will be in the bottom right corner of the screen.

// Let's make a few shapes
// define a circle with radius = 100
// Our new circle is called "tri"!!??!!
sf::CircleShape tri(100);

// A circle with 3 points
// There is a clue in the variable name
tri.setPointCount(3);

// Here is another "circle"
sf::CircleShape bumpy(100);
bumpy.setPointCount(15);

// Now lets have some variables for their positions (x and y)
float triX = 0;
float triY = 0;

float bumpyX = windowWidth;
float bumpyY = windowHeight;

Now we see something totally new to us. We declare four float variables as we have done on other occasions but each of the lines of code is prefixed with the const keyword. This makes the value that we initialize the variable to, unchangeable, constant. Technically speaking they are not actually variables(because they can’t vary); they are constants.

Now look at the values that we give these constants. Either -1 or 1. The exact use for these we will soon see. As a kind of advanced explanation, we use these values because when you multiply something by one it remains the same and when you multiply something by minus one it becomes its inverse. This simple fact is going to be useful.

It is a convention to name constants in all upper-case characters.

// Here are some variables that can never change.
// Thet are called constants.
// We make their names all uppercase to remind us.
// We use the const keyword to make them constant.
// We will see their purpose soon.
const float LEFT = -1;
const float RIGHT = 1;
const float UP = -1;
const float DOWN = 1;

Now we declare four more float variables, one for both the horizontal and vertical directions of travel for each of our two shapes. Notice that we assign their starting values using one of our four constants. These represent the starting directions of travel of our two shapes.

Lastly, in this next block of code, we declare and initialize a speed variable at  .5. This will be the number of pixels per frame that our shapes will move at. We will see, however, that we can modify this value using the following variables:

  • triDirectionX,
  • triDirectionY,
  • bumpyDirectionX
  • and bumpyDirectionY,

in order to make our shapes bounce all over the place.

// The direction is not constant
// tri starts with RIGHT and DOWN
float triDirectionX = RIGHT;
float triDirectionY = DOWN;

// The direction is not constant
// bumpy starts with LEFT and UP
float bumpyDirectionX = LEFT;
float bumpyDirectionY = UP;

// How fast will they move
float speed = .1;

Next, we enter the loop that will be continually repeated to create the frames of our game. We will finally demystify the details of loops in the next C++ tutorial but for now, all we need to know is everything in the remainder of our code is repeated over and over at least hundreds of frames per second.

// This "while" loop goes round and round- perhaps forever
// It only stops when the player closes the window
while (window.isOpen())
{

This next code we have also seen before and it causes our game to exit if the player closes the window. The next working project will go into this in greater detail including handling more input from the player.

// Has the player closed the game window?
sf::Event event;
while (window.pollEvent(event))
{
	if (event.type == sf::Event::Closed)
		// Someone closed the window- bye
		window.close();
}

This next block of code is the main learning point. Let’s examine the first if statement and then we can quickly understand them all. The code if(triX > windowWidth) tests for the triX variable being greater (higher) than the windowWidth variable. If the triX variable is greater then we can safely assume that the tri shape is about to disappear off of the right-hand side of the screen. Now look at the code that executes inside the if block should that condition be true. The code triDirectionX = LEFT assigns to triDirectionX the value in the LEFT constant, which is -1. Exactly what this does for us will become apparent in the next block of code

Before the next block of code, however, quickly look at the rest of the if statements. They all look for one of our shapes either exceeding the values stored in  windowWidth  windowHeight or going below (less than) zero. In each case, the appropriate value,  LEFT, RIGHT, UP or DOWN is assigned to the variable  triDrirectionX, triDirectionY, bumpyDirectionX or bumpyDirectionY.

The other thing to observe is that the if statements are paired with else if statements. This is because we know that, if for instance triX IS greater than windowWidth then it is a waste of time also checking whether triX is less than zero, in the same frame.

Take another look at the code that follows and if necessary refresh your memory with the following two C++ tutorials.

// Handle the ai, physics and process updates
// Watch out for collisions with the edge of the screen

// First the tri shape
if (triX > windowWidth) {
	// Triangle is near right of screen
	triDirectionX = LEFT;
}
else if (triX < 0) {
	// Triangle is near left of screen
	triDirectionX = RIGHT;
}

if (triY > windowHeight) {
	// Triangle is near bottom of screen
	triDirectionY = UP;
}
else if (triY < 0) {
	// Triangle is near top of screen
	triDirectionY = DOWN;
}

// Now for bumpy
if (bumpyX > windowWidth) {
	// Bumpy shape is near right of screen
	bumpyDirectionX = LEFT;
}
else if (bumpyX < 0) {
	// Bumpy shape is near left of screen
	bumpyDirectionX = RIGHT;
}

if (bumpyY > windowHeight) {
	// Bumpy shape is near bottom of screen
	bumpyDirectionY = UP;
}
else if (bumpyY < 0) {
	// Bumpy shape is near top of screen
	bumpyDirectionY = DOWN;
}

In the next code, all our hard work comes together. We manipulate each of the x and y coordinates of both of our shapes using the following formula:

coordinate x = coordinate x + speed * direction

A specific example we demonstrate better what is happening. If we assume the triX coordinate is currently equal to 120. That is, 120 pixels across from the left of the screen and it is travelling left. Where will we draw the tri shape on the x axis this frame? The sum goes like this:

new x location = 120 + .1 * -1

Just for clarity, the 120 is the current coordinate, the .1 is the value of the speed variable and the -1 is the value of triDirectionX because LEFT would have been assigned to it when it reached the far right of the screen. The answer is therefore:

new x location = 119.9

We can see that the coordinate is indeed moving steadily to the left.

This formula is repeated for each of the horizontal and vertical locations of both of our shapes.

Finally at the end of this block of code we call setPosition on each of our shapes to move them to their newly calculated positions.

// Let's move our two shapes based of the values
// held by the appropriate ...directionX and ...directionY
// and the speed variable
triX = triX + speed * triDirectionX;
triY = triY + speed * triDirectionY;

bumpyX = bumpyX + speed * bumpyDirectionX;
bumpyY = bumpyY + speed * bumpyDirectionY;

// Set the position of the shapes
tri.setPosition(triX, triY);
bumpy.setPosition(bumpyX, bumpyY);

In this last block of code, we clear the screen and actually draw the two shapes.

// Clear everything from the last run of the while loop
		window.clear();

		// Draw our game scene
		window.draw(tri);
		window.draw(bumpy);

		// Show everything we just drew
		window.display();

	}// This is the end of the "while" loop

	return 0;
}// End of the program

Summary and what’s next?

That was a big chunk of code but hopefully nothing too complicated. We now have shapes that bounce around the screen. We achieved this by moving the shapes each frame by a magnitude of .1 but by multiplying that magnitude by either 1 or -1 we controlled their up, down, left, right headings. We also used constants for the first time. Note that we could have not bothered with the constants and simply hard-coded the 1 or -1 values in the if statements where we changed the direction of our shapes. However, by using appropriately named constants we make it completely plain what they are for and what their values represent. This becomes more important as our code gets longer, spread over multiple files and more complex.

The next logical step is to read the tutorial on C++ loops which will be followed up by another SFML project where we will expand again on the functionality of our games.