24. Using Sprites to Shoot
How do we get sprites that we can shoot?
24.1. Getting Started
First, let’s go back to a program to start with.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | import random import arcade SPRITE_SCALING_PLAYER = 0.5 SPRITE_SCALING_COIN = 0.2 SPRITE_SCALING_LASER = 0.8 COIN_COUNT = 50 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 BULLET_SPEED = 5 class MyGame(arcade.Window): """ Main application class. """ def __init__(self): """ Initializer """ # Call the parent class initializer super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprites and Bullets Demo") # Variables that will hold sprite lists self.player_list = None self.coin_list = None # Set up the player info self.player_sprite = None self.score = 0 # Don't show the mouse cursor self.set_mouse_visible(False) arcade.set_background_color(arcade.color.AMAZON) def setup(self): """ Set up the game and initialize the variables. """ # Sprite lists self.player_list = arcade.SpriteList() self.coin_list = arcade.SpriteList() # Set up the player self.score = 0 # Image from kenney.nl self.player_sprite = arcade.Sprite("character.png", SPRITE_SCALING_PLAYER) self.player_sprite.center_x = 50 self.player_sprite.center_y = 70 self.player_list.append(self.player_sprite) # Create the coins for i in range(COIN_COUNT): # Create the coin instance # Coin image from kenney.nl coin = arcade.Sprite("coin_01.png", SPRITE_SCALING_COIN) # Position the coin coin.center_x = random.randrange(SCREEN_WIDTH) coin.center_y = random.randrange(SCREEN_HEIGHT) # Add the coin to the lists self.coin_list.append(coin) # Set the background color arcade.set_background_color(arcade.color.AMAZON) def on_draw(self): """ Render the screen. """ # This command has to happen before we start drawing arcade.start_render() # Draw all the sprites. self.coin_list.draw() self.player_list.draw() # Render the text arcade.draw_text(f"Score: {self.score}", 10, 20, arcade.color.WHITE, 14) def on_mouse_motion(self, x, y, dx, dy): """ Called whenever the mouse moves. """ self.player_sprite.center_x = x self.player_sprite.center_y = y def on_mouse_press(self, x, y, button, modifiers): """ Called whenever the mouse button is clicked. """ pass def update(self, delta_time): """ Movement and game logic """ # Call update on all sprites self.coin_list.update() def main(): window = MyGame() window.setup() arcade.run() if __name__ == "__main__": main() |
If you run this program, the player should move around the screen, and their should be coins. But not much else is happening yet.
Next, we need a ‚shooting‘ image:
Download this image (originally from Kenney.nl) and make sure it is in the same folder as your code.
24.2. Keeping The Player At The Bottom
Right now the player can move anywhere on the screen. We want to keep that sprite fixed to the bottom of the screen.
To do that, just remove the line of code for moving the player on the y-axis.
The player will keep the same y value that we set back in the setup
method.
def on_mouse_motion(self, x, y, dx, dy):
"""
Called whenever the mouse moves.
"""
self.player_sprite.center_x = x
# REMOVE THIS LINE: self.player_sprite.center_y = y
24.3. Moving The Coins Up
We want all the coins above the player. So we can adjust the starting y locations to have a starting point of 150 instead of 0. That will keep them above the player.
# Create the coins
for i in range(COIN_COUNT):
# Create the coin instance
# Coin image from kenney.nl
coin = arcade.Sprite("coin_01.png", SPRITE_SCALING_COIN)
# Position the coin
coin.center_x = random.randrange(SCREEN_WIDTH)
coin.center_y = random.randrange(150, SCREEN_HEIGHT)
# Add the coin to the lists
self.coin_list.append(coin)
24.4. Set Up Bullet List
We need to create a list to manage the bullets. There are four places we need to
add this bullet_list
code:
Create the
bullet_list
variable (Line 26)Create an instance of
SpriteList
(Line 44)Draw the bullet list (Line 83)
Update the bullet list (Line 105)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | import random import arcade SPRITE_SCALING_PLAYER = 0.5 SPRITE_SCALING_COIN = 0.2 SPRITE_SCALING_LASER = 0.8 COIN_COUNT = 50 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 BULLET_SPEED = 5 class MyGame(arcade.Window): """ Main application class. """ def __init__(self): """ Initializer """ # Call the parent class initializer super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprites and Bullets Demo") # Variables that will hold sprite lists self.player_list = None self.coin_list = None self.bullet_list = None # Set up the player info self.player_sprite = None self.score = 0 # Don't show the mouse cursor self.set_mouse_visible(False) arcade.set_background_color(arcade.color.AMAZON) def setup(self): """ Set up the game and initialize the variables. """ # Sprite lists self.player_list = arcade.SpriteList() self.coin_list = arcade.SpriteList() self.bullet_list = arcade.SpriteList() # Set up the player self.score = 0 # Image from kenney.nl self.player_sprite = arcade.Sprite("character.png", SPRITE_SCALING_PLAYER) self.player_sprite.center_x = 50 self.player_sprite.center_y = 70 self.player_list.append(self.player_sprite) # Create the coins for i in range(COIN_COUNT): # Create the coin instance # Coin image from kenney.nl coin = arcade.Sprite("coin_01.png", SPRITE_SCALING_COIN) # Position the coin coin.center_x = random.randrange(SCREEN_WIDTH) coin.center_y = random.randrange(150, SCREEN_HEIGHT) # Add the coin to the lists self.coin_list.append(coin) # Set the background color arcade.set_background_color(arcade.color.AMAZON) def on_draw(self): """ Render the screen. """ # This command has to happen before we start drawing arcade.start_render() # Draw all the sprites. self.coin_list.draw() self.player_list.draw() self.bullet_list.draw() # Render the text arcade.draw_text(f"Score: {self.score}", 10, 20, arcade.color.WHITE, 14) def on_mouse_motion(self, x, y, dx, dy): """ Called whenever the mouse moves. """ self.player_sprite.center_x = x def on_mouse_press(self, x, y, button, modifiers): """ Called whenever the mouse button is clicked. """ pass def update(self, delta_time): """ Movement and game logic """ # Call update on all sprites self.coin_list.update() self.bullet_list.update() def main(): window = MyGame() window.setup() arcade.run() if __name__ == "__main__": main() |
24.5. Creating Bullets
We need to
create bullets when the user presses the mouse button. We can add an
on_mouse_press
method to do something when the user presses the mouse button:
def on_mouse_press(self, x, y, button, modifiers):
# Create a bullet
bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)
# Add the bullet to the appropriate list
self.bullet_list.append(bullet)
This will create a bullet, but the bullet will default to the lower left corner. You can just barely see it.
We can give the bullet a position:
def on_mouse_press(self, x, y, button, modifiers):
# Create a bullet
bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)
bullet.center_x = x
bullet.center_y = y
# Add the bullet to the appropriate list
self.bullet_list.append(bullet)
But this isn’t what we want either. The code above puts the laser where we click the mouse. We want the laser to be where the player is. That’s easy:
def on_mouse_press(self, x, y, button, modifiers):
# Create a bullet
bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)
bullet.center_x = self.player_sprite.center_x
bullet.center_y = self.player_sprite.center_y
# Add the bullet to the appropriate list
self.bullet_list.append(bullet)
We can even start the bullet a bit ABOVE the player:
def on_mouse_press(self, x, y, button, modifiers):
# Create a bullet
bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)
bullet.center_x = self.player_sprite.center_x
bullet.center_y = self.player_sprite.center_y + 30
# Add the bullet to the appropriate list
self.bullet_list.append(bullet)
We can make the bullet move up using the constant BULLET_SPEED
which
we set to 5 at the top of the program:
def on_mouse_press(self, x, y, button, modifiers):
# Create a bullet
bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)
bullet.center_x = self.player_sprite.center_x
bullet.center_y = self.player_sprite.center_y + 30
bullet.change_y = BULLET_SPEED
# Add the bullet to the appropriate list
self.bullet_list.append(bullet)
We can rotate the bullet so it isn’t sideways using the angle
attribute
built into the Sprite
class:
def on_mouse_press(self, x, y, button, modifiers):
# Create a bullet
bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)
bullet.center_x = self.player_sprite.center_x
bullet.center_y = self.player_sprite.center_y + 30
bullet.change_y = BULLET_SPEED
bullet.angle = 90
# Add the bullet to the appropriate list
self.bullet_list.append(bullet)
24.6. Bullet Collisions
Now that we have bullets, how do we get them to collide with the coins?
We add the following to our applications update
method:
# Loop through each bullet
for bullet in self.bullet_list:
# Check this bullet to see if it hit a coin
hit_list = arcade.check_for_collision_with_list(bullet, self.coin_list)
# If it did, get rid of the bullet
if len(hit_list) > 0:
bullet.remove_from_sprite_lists()
# For every coin we hit, add to the score and remove the coin
for coin in hit_list:
coin.remove_from_sprite_lists()
self.score += 1
# If the bullet flies off-screen, remove it.
if bullet.bottom > SCREEN_HEIGHT:
bullet.remove_from_sprite_lists()
We loop through each bullet with a for
loop. Then we check to see if the
bullet is hitting any of the coins. If it is, we get rid of the coin. We get
rid of the bullet.
We also check to see if the bullet flies off the top of the screen. If it does, we get rid of the bullet. This is easy to forget, but if you do, it will cause the computer to slow down because you are tracking thousands of bullets that have long ago left the space we care about.
Here’s the full example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | import random import arcade SPRITE_SCALING_PLAYER = 0.5 SPRITE_SCALING_COIN = 0.2 SPRITE_SCALING_LASER = 0.8 COIN_COUNT = 50 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 BULLET_SPEED = 5 class MyGame(arcade.Window): """ Main application class. """ def __init__(self): """ Initializer """ # Call the parent class initializer super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprites and Bullets Demo") # Variables that will hold sprite lists self.player_list = None self.coin_list = None self.bullet_list = None # Set up the player info self.player_sprite = None self.score = 0 # Don't show the mouse cursor self.set_mouse_visible(False) arcade.set_background_color(arcade.color.AMAZON) def setup(self): """ Set up the game and initialize the variables. """ # Sprite lists self.player_list = arcade.SpriteList() self.coin_list = arcade.SpriteList() self.bullet_list = arcade.SpriteList() # Set up the player self.score = 0 # Image from kenney.nl self.player_sprite = arcade.Sprite("character.png", SPRITE_SCALING_PLAYER) self.player_sprite.center_x = 50 self.player_sprite.center_y = 70 self.player_list.append(self.player_sprite) # Create the coins for i in range(COIN_COUNT): # Create the coin instance # Coin image from kenney.nl coin = arcade.Sprite("coin_01.png", SPRITE_SCALING_COIN) # Position the coin coin.center_x = random.randrange(SCREEN_WIDTH) coin.center_y = random.randrange(120, SCREEN_HEIGHT) # Add the coin to the lists self.coin_list.append(coin) # Set the background color arcade.set_background_color(arcade.color.AMAZON) def on_draw(self): """ Render the screen. """ # This command has to happen before we start drawing arcade.start_render() # Draw all the sprites. self.coin_list.draw() self.bullet_list.draw() self.player_list.draw() # Render the text arcade.draw_text(f"Score: {self.score}", 10, 20, arcade.color.WHITE, 14) def on_mouse_motion(self, x, y, dx, dy): """ Called whenever the mouse moves. """ self.player_sprite.center_x = x def on_mouse_press(self, x, y, button, modifiers): """ Called whenever the mouse button is clicked. """ # Create a bullet bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER) # The image points to the right, and we want it to point up. So # rotate it. bullet.angle = 90 # Position the bullet bullet.center_x = self.player_sprite.center_x bullet.bottom = self.player_sprite.top # Add the bullet to the appropriate lists self.bullet_list.append(bullet) def update(self, delta_time): """ Movement and game logic """ # Call update on all sprites self.coin_list.update() self.bullet_list.update() # Loop through each bullet for bullet in self.bullet_list: # Check this bullet to see if it hit a coin hit_list = arcade.check_for_collision_with_list(bullet, self.coin_list) # If it did, get rid of the bullet if len(hit_list) > 0: bullet.remove_from_sprite_lists() # For every coin we hit, add to the score and remove the coin for coin in hit_list: coin.remove_from_sprite_lists() self.score += 1 # If the bullet flies off-screen, remove it. if bullet.bottom > SCREEN_HEIGHT: bullet.remove_from_sprite_lists() def main(): window = MyGame() window.setup() arcade.run() if __name__ == "__main__": main() |
24.7. Other Examples
Here are some other examples from Arcade Academy and its Example List: