Game Design: Shooting Bullets
Game Design: Shooting Bullets
We will now expand our game by making the troopers move downwards while BB8 will be controlled to move left or right by arrow keys. We will move BB8 down to the bottom of the screen but allow him to shoot at troopers whenever the space bar is clicked. A point will be subtracted from the score each time a bullet is shot but two points will be added whenever a trooper is shot. If a trooper ever runs into BB8, a game over screen will be displayed along with the final score and instructions about how to restart the game. The final game will look like the following.
We will also use the bullet.png image in the Images folder.
There is nothing to Fork/Clone from Github this time. Instead, you should duplicate your BB8 Attack Game and rename BB8 Shooter Game. We will be updating that code as you go through this chapter.
#8 and #11: We start by adding two more constants. bullet_scale which you can use to scale your bullet sprite and SP which will be the speed of BB8.
Instead of following the mouse, we want BB8 to move back and forth across the bottom of the screen by use of the left and right arrow keys. If it goes off the right edge it will come back through on the left edge and visa versa. To do this, instead of our pass placeholder in the update() method of the Player() class we need to enter some code that will move them. We will worry about the key strokes later in the code. Below we will just work on the update section that will be called whenever the key is pressed. See below.
#19: This will add change_x to the center_x position of BB8 every time the right or left key is pressed.
#20-21: Each sprite has top, right, bottom and left boundaries that we can call. Here we check if the self.right of BB8 is left of the screen. If it is, we will place BB8 so it's left side is even with the right edge of the screen.
#22-23: Here we check if the self.left of BB8 is right of the screen. If it is, we will place BB8 so it's right side is even with the left edge of the screen.
In this new game we don't want the troopers to remain static. We would like them to be moving down the screen. If they go off the bottom they will re-spawn somewhere above the screen. To do this, instead of our pass placeholder in the update( ) method of the Trooper( ) class we need to enter some code that will move them.
#33: This will subtract 2 from the center_y position of each trooper
#34: Again, each sprite has top, right, bottom and left boundaries that we can call. Here we check if the self.top of the trooper is below the bottom of the screen. If it is we will do the following:
#35: Set the center_x position of the trooper randomly between the width of the trooper and the Screen Width - width of the trooper.
#36: Set the center_y position of the trooper randomly between the Screen Height + the height of the trooper and 2 times the Screen Height.
We will be shooting bullets that will be traveling up the screen. If the bullet doesn't hit anything it needs to be destroyed when it gets to the top of the screen. If they go off the bottom they will re-spawn somewhere above the screen. To do this, we need to add a new Bullet class.
#40-42: Create the class Bullet() and initialize it with the bullet.png image and bullet_scale.
#45: In it's update() method, add 10 to the bullet's self.center_y position on each update. It would probably be considered better programming to put the 10 into a bullet_speed variable in the constants section. (Sorry I'm so lazy!)
#46-47: Check to see if the self.bottom of the bullet is above the screen height. If it is, then call the kill() method on the bullet sprite.
We just need to make a few modifications to the reset() method of our game:
#63: We want to create a variable called self.Gameover and set it to False. Later, we will determine what ends the game and set this variable to True and be able to show a different screen.
#68: Create the bullets SpriteList().
#73-77: Only change here is to start BB8 at the bottom of the screen using self.BB8.bottom = 2.
#80-85: Only change here is to start troopers .center_y positions anywhere from top half of the screen all the way up to twice the height of the screen.
We will make two simple modifications and one bigger one to the on_draw() method:
#91: Add the drawing of the bullets list.
#95: Draw the score on the screen. This time we used positions that will change when the Screen width and height changes. This will always print the score in the top right corner.
#97-100: Here is our bigger change. If the self.Gameover variable ever gets set to True, we will draw an overlay screen with results and further directions. Everything here is probably pretty straight forward. We covered the screen with a black rectangle. We wrote some green text telling the user to Press P to play again. We will take care of that later in the code. Finally we wrote some more text showing the final score.
We have several changes we need to make in the on_update() method.
#107: Add the updating of the bullets list.
#109-110: If we shoot all of the troopers before any trooper kills BB8 the game will be over. For this reason we want to check the length of the trooper_list. At the beginning of the game it held 40 troopers but every time we shoot one we will kill that particular sprite. Here we check if there are no more troopers in the list. If so, we set the self.Gameover variable to True.
#113-116: Here we check if a trooper in the trooper_list has hit our self.BB8 player. The check_for_collision_with_list() method will create a list which we are naming bb8_hit. This list technically would be made up of any troopers from the trooper_list that crashed into our player. However, it only takes one trooper to kill us so we will immediately check if the length of bb8_hit is greater than zero and if it is we will kill our player and set the self.Gameover variable to True.
#119: To check if any bullets are hitting any troopers we need to loop through the self.bullets list. We will do this with a for loop by writing "for bullet in self.bullets:". We could have used anything for the word bullet (ex: item, shot, b). The following lines of code are all within this for loop.
#121: Make a hit_list of troopers that have collided with a bullet.
#124-125: Check the length of this hit_list. If it is greater than 0 then delete (kill) the bullet.
#128-130: Iterate through the hit_list by using "for trooper in hit_list:". Again, any word could be used in place of trooper but this word makes sense because it is troopers in the hit_list. Kill any troopers in the hit_list because they were put in that list because they collided with a bullet. Add two to the score. We will be adding 2 if we shoot a trooper and -1 when we shoot a bullet.
#133: We will be using keys to move BB8 rather than the mouse this time. Instead of the on_mouse_motion() method we will be using on_key_press() and on_key_release() methods.
#134-140: There are four key strokes we need to handle. (RIGHT, LEFT, P and SPACE). If the LEFT key is pressed modify the change_x of the BB8 by -SP. If the RIGHT key is pressed modify the change_x of the BB8 by +SP. If the P key is pressed, call self.reset() of the game. On line #140 check if the SPACE key is pressed and the Gameover variable is False. If the game was over we wouldn't want bullets still being fired when the space bar is pressed.
#142: If the SPACE key is pressed we need to instantiate a bullet.
#143: Place the center_x of the bullet on the center_x of BB8.
#144: Place the bottom of the bullet at the top of BB8
#145: Rotate the bullet 90˚.
#146: Append the bullet to the bullets list
#147: Subtract 1 from the score for firing a bullet.
#149-151: Using on_key_release, if the LEFT or RIGHT keys are released set the change_x of BB8 to zero so it won't be moving.
Finally, we will run the main function. We can change the game title to "BB8 Shooter".
When you get this game to work, demonstrate it to your instructor. Feel free to implement any new knowledge into your game project. Read on if you want to make the shooting fully automatic.
If you are interested in how to make your shooting fully automatic by just holding down the space bar you can make another function in the MyGame class. I called it fullauto and moved the instantiation of the bullets into this function. Even though it doesn't look like we are using the "dt" parameter it needs to be present because we will be using arcade.schedule which needs to send a time parameter to know how often to call it.
Now when we press the SPACE bar we need to call arcade.schedule with the fullauto function as the first parameter and a time delay for the second parameter. I'm sending in 0.1 seconds between bullets being fired. When we release the SPACE key, we actually have to unschedule the fullauto function or it will lead to crashes as you can read in the documentation.