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”]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.
- 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.
- Now click OK.
- 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.
- 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.
- 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.
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.
Hello John,
Thanks for this tutorial too, really good!
Two observations:
– typo: // And has the title “Conditions and —> brnching demo”
– what is the reason for using else if instead of simple else?
Thanks for pointing this and other typos out. It is much appreciated. The else if code is used otherwise, for example, whenever triX > windowWidth wasn’t true triDirectionX would be set to RIGHT. This would happen when the shape is meant to be travelling left as well. Hope this helps. Thanks again.
Cheers. I’ve tested like this and it gave me a build error
if (triX > windowWidth) triDirectionX = LEFT; // close to the right edge
else (triX < 0) triDirectionX = RIGHT; // close to the left edge
Thank you again.
Hi Zoltan, What is the error. I will try and help.
Hi John – Thanks for excellent website. I really appreciate and enjoy it. These are my first steps with C++.
I did this project and it works well – but one thing is not clear for me. Both shapes, while bouncing the top and left wall- they bounce once they just touch the wall, while when going towards right and bottom wall – they go beyond it (i.e. hide) and reverse the direction only when the last pixel disappears.
I work on OSX and Xcode. I corss checked it by copying your code and this is exactly the same so no typing error on my end.
Do you have any idea why is that?
Cheers,
Swnms
There are two possible things going on. Firstly, the resolution the window is set to at the start of the code might be different to your monitor. Also, all SFML shapes and other graphical objects as well have an origin. This is the point at which the shape is drawn. The shapes are obviously bigger than one pixel so the position of the origin has an effect. The default origin is the top left pixel which is why they bounce nicely on the top and left but dissappear on the bottom and right. SFML provides a way to choose the origin so you can give each shape the origin that you prefer.
You could try writing something like this just after you declare tri.
tri.setOrigin(50, 50);
I hope this helps a bit.
Thak you a lot John, i’m on my day one C++ learning and i’m loving your tutorials. One thing which bothered me a lot while the balls where bouncing around was that they went outside of the window while going down and right (well X+ and Y+) so after a bit of head scratching, a couple more variables and some adjustments i was able to fix it and they now stay inside every side of window and i’m very proud of myself all thanks to your tutorials!
Cheers from Italy!
Thanks for your comment. Yes, it is really satisfying to solve a problem. The longer it takes the bigger the high when you succeed.
Good luck with your programming,
John