Growing the branches

Next, as I have been promising for around the last seventeen pages, we will use all the new C++ techniques to draw and move some branches on our tree.

Add this code outside the main function. Just to be absolutely clear, I mean before the code int main():

#include "stdafx.h" 
#include <sstream> 
#include <SFML/Graphics.hpp> 
 
using namespace sf; 
 
// Function declaration
void updateBranches(int seed);

const int NUM_BRANCHES = 6;
Sprite branches[NUM_BRANCHES];

// Where is the player/branch?
// Left or Right
enum class side { LEFT, RIGHT, NONE };
side branchPositions[NUM_BRANCHES]; 
 
int main() 
{ 

We just achieved quite a few things with that new code:

  • First, we wrote a function prototype for a function called updateBranches. We can see that it does not return a value (void) and it takes an int argument called seed. We will write the function definition soon and we will then see exactly what it does.
  • Next, we declare a constant int called NUM_BRANCHES and initialize it to 6. There will be six moving branches on the tree and we will soon see how NUM_BRANCHES will be useful to us.
  • Following this, we declare an array of Sprite objects called branches that can hold six Sprites.
  • After that, we declare a new enumeration called side with three possible values, LEFT, RIGHT, and NONE. This will be used to describe the position of individual branches as well as the player, in a few places throughout our code.
  • Finally, in the previous new code, we initialize an array of side types, with a size of NUM_BRANCHES (6). To be clear about what this achieves; we will have an array called branchPositions with six values in it. Each of these values is of the type side, which can be either, LEFT, RIGHT, or NONE.

Note

Of course, what you really want to know is why the constant, the two arrays, and the enumeration were declared outside the main function. By declaring them above main they now have global scope. Or, describing it another way, the constant, the two arrays, and the enumeration have scope for the entire game. This will mean we can access and use them all anywhere in the main function and in the updateBranches function. Note that it is good practice to make all variables as local to where they are actually used as possible. It might seem useful to make everything global but this leads to hard-to-read and error-prone code.

Preparing the branches

Now we will prepare our six Sprite objects and load them into the branches array. Add the highlighted code just before our game loop:

// Position the text 
FloatRect textRect = messageText.getLocalBounds(); 
messageText.setOrigin(textRect.left + 
   textRect.width / 2.0f, 
   textRect.top + 
   textRect.height / 2.0f); 
 
messageText.setPosition(1920 / 2.0f, 1080 / 2.0f); 
 
scoreText.setPosition(20, 20); 
 
// Prepare 6 branches
Texture textureBranch;
textureBranch.loadFromFile("graphics/branch.png");

// Set the texture for each branch sprite
for (int i = 0; i < NUM_BRANCHES; i++) 
{   
  branches[i].setTexture(textureBranch);   
  branches[i].setPosition(-2000, -2000);   
  // Set the sprite's origin to dead center   
  // We can then spin it round without changing its position 
  branches[i].setOrigin(220, 20);
} 
 
while (window.isOpen()) 
{ 

The previous code does not use any new concepts. First we declare an SFML Texture object and load the branch.png graphic into it.

Next, we create a for loop, which sets i to zero and increments i by one each pass through the loop until i is no longer less than NUM_BRANCHES. This is exactly right because NUM_BRANCHES is 6 and the branches array has positions 0 through 5.

Inside the for loop we set the Texture for each Sprite in the branches array with setTexture and then hide it off screen with setPosition.

Finally, we set the origin (the point which is located when it is drawn), with setOrigin, to the center of the sprite. Soon, we will be rotating these sprites and having the origin in the center means they will spin nicely around, without moving the sprite out of position.

Updating the branch sprites each frame

In this next code we set the position of all the sprites in the branches array, based upon their position in the array and the value of side in the corresponding branchPositions array. Add the highlighted code and try to understand it, then we can go through it in detail:

   // Update the score text 
   std::stringstream ss; 
   ss << "Score: " << score; 
   scoreText.setString(ss.str()); 
 
   // update the branch sprites   
   for (int i = 0; i < NUM_BRANCHES; i++)   
   {     
     float height = i * 150;     
     if (branchPositions[i] == side::LEFT)     
     {        
       // Move the sprite to the left side        
       branches[i].setPosition(610, height);

       // Flip the sprite round the other way        
       branches[i].setRotation(180);     
     }

     else if (branchPositions[i] == side::RIGHT)     
     {        
       // Move the sprite to the right side        
       branches[i].setPosition(1330, height);    
    
       // Set the sprite rotation to normal        
       branches[i].setRotation(0);     
     }     
     else     
     {        
       // Hide the branch        
       branches[i].setPosition(3000, height);     
     }   
   } 
} // End if(!paused) 
 
/* 
**************************************** 
Draw the scene 
**************************************** 

The code we just added is one big for loop that sets i to zero, increments i by 1 each time through the loop, and keeps going until i is no longer less than 6.

Inside the for loop a new float variable called height is set to i * 150. This means that the first branch will have a height of 0, the second of 150, and the sixth of 750.

Next we have a structure of if and else blocks. Look at the structure with the code stripped out:

if() 
{ 
} 
else if() 
{ 
} 
else 
{ 
} 

The first if uses the branchPositions array to see whether the current branch should be on the left. If it is, it sets the corresponding Sprite from the branches array to a position on the screen, appropriate for the left (610 pixels) and whatever the current height is. It then flips the sprite by 180 degrees because the branch.png graphic hangs to the right by default.

The else if only executes if the branch is not on the left. It uses the same method to see if it is on the right. If it is then the branch is drawn on the right (1330 pixels). Then the sprite rotation is set to 0 degrees, just in case it had previously been at 180 degrees. If the x coordinate seems a little bit strange, just remember that we set the origin for the branch sprites to their center.

The final else assumes, correctly, that the current branchPosition must be NONE and hides the branch off screen at 3000 pixels.

At this point, our branches are in position, ready to be drawn.

Drawing the branches

Here we use another for loop, to step through the entire branches array from 0 to 5 and draw each branch sprite. Add the following highlighted code:

// Draw the clouds 
window.draw(spriteCloud1); 
window.draw(spriteCloud2); 
window.draw(spriteCloud3); 
 
// Draw the branches
for (int i = 0; i < NUM_BRANCHES; i++) 
{   
  window.draw(branches[i]);
} 
 
// Draw the tree 
window.draw(spriteTree); 

Of course we still haven't written the function that actually moves all the branches. Once we have written that function, we will also need to work out when and how to call it. Let's solve the first problem and write the function.

Moving the branches

We have already added the function prototype above the main function. Now we code the actual definition of the function that will move all the branches down by one position each time it is called. We will code this function in two parts so we can more easily examine what is happening.

Add the first part of the updateBranches function after the closing curly brace of the main function:

// Function definition 
void updateBranches(int seed) 
{ 
   // Move all the branches down one place 
   for (int j = NUM_BRANCHES-1; j > 0; j--) 
   {    
      branchPositions[j] = branchPositions[j - 1]; 
   } 
} 

In this first part of the function, we simply move all the branches down one position, one at a time, starting with the sixth branch. This is achieved by making the for loop count from 5 through to 0. The code branchPositions[j] = branchPositions[j - 1]; makes the actual move.

The other thing to note, with the previous code, is that, after we have moved the branch in position 4 to position 5, then the branch in position 3 to position 4, and so on, we will need to add a new branch at position 0, which is the top of the tree.

Now we can spawn a new branch at the top of the tree. Add the highlighted code and then we will talk about it:

// Function definition 
void updateBranches(int seed) 
{ 
   // Move all the branches down one place 
   for (int j = NUM_BRANCHES-1; j > 0; j--) 
   {    
      branchPositions[j] = branchPositions[j - 1]; 
   } 
 
   // Spawn a new branch at position 0   
   // LEFT, RIGHT or NONE   
   srand((int)time(0)+seed);   
   int r = (rand() % 5);   
   switch (r) 
   {   
   case 0:     
     branchPositions[0] = side::LEFT;     
     break;   

   case 1:     
     branchPositions[0] = side::RIGHT;     
     break;   

   default:     
     branchPositions[0] = side::NONE;     
     break;  
    } 
} 

In the final part of the updateBranches function, we use the integer seed variable that gets passed in with the function call. We do this to guarantee that the random number seed is always different and we will see how this value is arrived at in the next chapter.

Next, we generate a random number between zero and four and store the result in the int variable r. Now we switch using r as the expression.

The case statements mean that, if r is equal to zero then we add a new branch on the left-hand side at the top of the tree. If r is equal to 1 then the branch goes on the right. If r is anything else, (2, 3, or 4) then the default ensures that no branch will be added at the top. This balance of left, right, and none makes the tree seem realistic and the game works quite well. You could easily change the code to make the branches more frequent or less so.

Even after all this code for our branches, we still can't glimpse a single one of them in the game. This is because we have more work to do before we can actually call updateBranches.

If you really want see a branch now, you can add some temporary code and call the function five times with a unique seed each time, just before the game loop:

updateBranches(1);
updateBranches(2);
updateBranches(3);
updateBranches(4);
updateBranches(5); 
 
while (window.isOpen()) 
{ 

You can now see the branches in their place. But if the branches are to actually move we will need to call updateBranches on a regular basis.

Moving the branches

Tip

Don't forget to remove the temporary code before moving on.

Now we can turn our attention to the player and call the updateBranches function for real.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.178.243