11 Sprites¶
Our games need support for handling objects that collide. Balls bouncing off paddles, laser beams hitting aliens, or our favorite character collecting a coin. All these examples require collision detection.
The Arcade library has support for sprites. A sprite is a two dimensional image that is part of the larger graphical scene. Typically a sprite will be some kind of object in the scene that will be interacted with like a car, frog, or little plumber guy.
Originally, video game consoles had built-in hardware support for sprites. Now this specialized hardware support is no longer needed, but we still use the term “sprite.” The history of sprites is interesting, if you want to read up more about it.
11.1 Basic Sprites and Collisions¶
Let’s step through an example program that uses sprites. This example shows how to create a screen of sprites that are coins, and collect them using a sprite that is a character image controlled by the mouse as shown in the figure below. The program keeps “score” on how many coins have been collected. The code for this example may be found at:
http://arcade.academy/examples/sprite_collect_coins.html
In this chapter, we’ll step through that example.
11.1.1 Getting the Application Started¶
The first few lines of our program start off like other games we’ve done. We import a couple libraries. Set a couple constants for the size of the screen, and a new constant that we will use to scale our graphics to half their original size.
import random
import arcade
SPRITE_SCALING = 0.5
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class MyApplication(arcade.Window):
# --- Class methods will go here
window = MyApplication(SCREEN_WIDTH, SCREEN_HEIGHT)
window.setup()
arcade.run()
11.1.2 The Constructor¶
What’s next? We need to add our methods to the MyApplication
class.
We’ll start with our __init__
method. This is the method we use to
initialize our variables. Here it is:
def __init__(self, width, height):
# Call the parent class initializer
super().__init__(width, height)
# Variables that will hold sprite lists
self.all_sprites_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)
# Set the background color
arcade.set_background_color(arcade.color.AMAZON)
The variables we are creating:
all_sprites_list
: This is a special list that we will add all our sprites to. By having all the sprites in a single list, we can draw them all in a single command.coin_list
: This is a list of all the coins. We wille be checking if the player touches any sprite in this list.player_sprite
: This points to our player’s sprite. It is the sprite we will move, and we’ll check to see if itscore
: This keeps track of our score.
We use a command built into the parent Window
class called
set_mouse_visible
to make the mouse not visible. Finally we set the
background color.
11.1.3 The Setup Function¶
Next up, we have a setup
method. In the first code example, we have the
code that calls this function near the end: window.setup()
.
This setup code
could be moved into the __init__
method. Why is it separate? Later on
if we want to add the ability to “play again”, we can just call the setup
function. If the code to set up the window is mixed with the code to set
up the game, then it is more difficult to program that functionality. Here
we start by keeping them separate.
def setup(self):
""" Set up the game and initialize the variables. """
# Sprite lists
self.all_sprites_list = arcade.SpriteList()
self.coin_list = arcade.SpriteList()
# Set up the player
self.score = 0
self.player_sprite = arcade.Sprite("images/character.png", SPRITE_SCALING)
self.player_sprite.center_x = 50
self.player_sprite.center_y = 50
self.all_sprites_list.append(self.player_sprite)
for i in range(50):
# Create the coin instance
coin = arcade.Sprite("images/coin_01.png", SPRITE_SCALING / 3)
# Position the coin
coin.center_x = random.randrange(SCREEN_WIDTH)
coin.center_y = random.randrange(SCREEN_HEIGHT)
# Add the coin to the lists
self.all_sprites_list.append(coin)
self.coin_list.append(coin)
How does this code work?
First, we need some lists to hold our sprites. We could do something like this:
all_sprites_list = []
But wait! all_sprites_list
is an instance variable that’s part of our class.
we need to prepend it with self.
.
self.all_sprites_list = []
However, the Arcade library has a class especially for handling sprite lists.
This class is called SpriteList
. So instead of creating an empty list with
[]
, we will create a new instance of the SpriteList
class:
self.all_sprites_list = SpriteList()
Except that doesn’t work. Why? SpriteList
is in the Arcade library. We
need to prepend any reference to things in the Arcade library with arcade
of course, so now we have:
self.all_sprites_list = arcade.SpriteList()
We need a separate list for just coins. This list won’t have the player. We also need to reset our score to 0.
self.coin_list = arcade.SpriteList()
self.score = 0
Now we need to create out sprite. The name of the class that represents sprites
is called Sprite
. It takes two paramters. A path to the image we will be
using, and how big to scale it.
self.player_sprite = arcade.Sprite("images/character.png", SPRITE_SCALING)
Ok, so if you are following along, you’ll need to download the images. You can right-click on the two images below and save them. The images come from kenney.nl who has a lot of free and cheap game image assets that you can use in your games.
Where should you save them? It depends. If you load your sprite with the code
below, the computer will look for the character.png
image in the same
directory as your Python file. Save the image anywhere else, and it won’t
be found.
self.player_sprite = arcade.Sprite("character.png", SPRITE_SCALING)
I don’t like putting my images with my code. By the time I finish a game there’s a lot of images, sounds, and other files all mixed together. Instead I like to create subdirectories for images and sounds. You can do this by creating a subdirectory called “images” and them prepending “images/” to your filename.
self.player_sprite = arcade.Sprite("images/character.png", SPRITE_SCALING)
11.1.4 The On Draw Method¶
How do we draw all our sprites? Really easy. We just override the on_draw
method and call the draw
method in our sprites list. That method will
loop throug all our sprites for us, and draw them.
def on_draw(self):
arcade.start_render()
# Draw all the sprites.
self.all_sprites_list.draw()
Woohoo! That was easy.
In addition to drawing the sprites, let’s go ahead and put the score on the screen:
# Put the text on the screen.
output = "Score: " + str(self.score)
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)
Rather than do that "Score: " + str(self.score)
it is possible to do
print formatting if you are using Python 3.6 or later. We’ll talk more about
print formatting later, but that code would look like:
# Put the text on the screen.
output = f"Score: {self.score}"
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)
There are three standards for how to format strings in Python, so that whole subject is a bit confusing.
11.1.5 The On Mouse Motion Method¶
Moving the player sprite with the mouse is easy. All sprites have instance
variables center_x
and center_y
. Just change those values to the mouse’s
x and y location to move the sprite.
def on_mouse_motion(self, x, y, dx, dy):
self.player_sprite.center_x = x
self.player_sprite.center_y = y
11.1.6 The Animate Method¶
Our animate
method needs to do three things:
- Update the sprites
- Check to see if the player is touching any coins
- Remove any coins colliding with the player, and update the score.
Each sprite has its own update
method. This allows sprites to move and
animate its images. Right now, our sprite does not have this method. But we
will soon. Rather than call the update
method of each sprite we have,
there is an update
method in each sprite list that will call update
on each sprite in the list. Therefore, just calling update
with our
all_sprites_list
will cause all sprites to update.
self.all_sprites_list.update()
How do we detect what coins are touching the player? We call the
check_for_collision_with_list
method. Pass it in our player sprite,
along with a list of all the coins. That function will return a list of
all colliding sprites. If no sprites collide, the list will be empty.
# Generate a list of all sprites that collided with the player.
hit_list = arcade.check_for_collision_with_list(self.player_sprite,
self.coin_list)
What do we do with this hit_list
we get back? We loop through it. We add one
to the score for each sprite hit.
We also need to get rid of the sprite. The sprite class has a method called
kill
. This method will remove the sprite from existance.
# Loop through each colliding sprite, remove it, and add to the score.
for coin in hit_list:
coin.kill()
self.score += 1
Here’s the whole animate
method put together:
def animate(self, delta_time):
""" Movement and game logic """
# Call update on all sprites (The sprites don't do much in this
# example though.)
self.all_sprites_list.update()
# Generate a list of all sprites that collided with the player.
hit_list = arcade.check_for_collision_with_list(self.player_sprite,
self.coin_list)
# Loop through each colliding sprite, remove it, and add to the score.
for coin in hit_list:
coin.kill()
self.score += 1
11.2 Moving Sprites¶
How do we get sprites to move? Start the code from our original example here:
http://arcade.academy/examples/sprite_collect_coins.html
To customize our sprite’s behavior, we need to subclass the Sprite
class
with our own child class. This is easy:
class Coin(arcade.Sprite):
We need to provide each sprite with a
update
method. The update
method is automatically called to update
the sprite’s position.
class Coin(arcade.Sprite):
def update(self):
# Code to move goes here
Wait! We have a new class called Coin, but we aren’t using it. Find in our original code this line:
coin = arcade.Sprite("images/coin_01.png", SPRITE_SCALING / 3)
See how it is creating an instance of Sprite
? We want to create an instance
of our new Coin
class instead:
coin = Coin("images/coin_01.png", SPRITE_SCALING / 3)
Now, how do we get the coin to move?
11.2.1 Moving Sprites Down¶
To get the sprites to “fall” down the screen, we need to make their y location
smaller. This is easy. Over-ride update
in the sprite and subtract from
y each frame:
class Coin(arcade.Sprite):
def update(self):
self.center_y -= 1
This causes the coins to move down. But once they move off the screen they keep going into negative-coordinate land. We can’t see them any more. Sad.
We can get around this by resetting the coins up to the top. Here’s how its done:
class Coin(arcade.Sprite):
def update(self):
self.center_y -= 1
# See if we went off-screen
if self.center_y < 0:
self.center_y = SCREEN_HEIGHT
But this isn’t perfect. Because if your eyes are fast, you can see the coin ‘pop’ in and out of existence at the edge. It doesn’t smoothly slide off. This is because we move it when the center of the coin is at the edge. Not the top of the coin has slid off.
There are a couple ways we can do this. Here’s one. We’ll check at -20 instead of 0. As long as the coin radius is 20 or less, we are good.
class Coin(arcade.Sprite):
def update(self):
self.center_y -= 1
# See if we went off-screen
if self.center_y < -20:
self.center_y = SCREEN_HEIGHT + 20
There’s another way. In addition to center_y
, sprites have other
members that are useful in these cases. They are top
, bottom
,
left
and right
. So we can do this:
class Coin(arcade.Sprite):
def update(self):
self.center_y -= 1
# See if we went off-screen
if self.top < 0:
self.bottom = SCREEN_HEIGHT
Doing this allows the coins to smoothly slide on and off the screen. But since they reappear at the top, we get repeating patters. See the image below:
Instead we can randomize it a bit:
def update(self):
# Move the coin
self.center_y -= 1
# See if the coin has fallen off the bottom of the screen.
# If so, reset it.
if self.top < 0:
# Reset the coin to a random spot above the screen
self.center_y = random.randrange(SCREEN_HEIGHT + 20,
SCREEN_HEIGHT + 100)
self.center_x = random.randrange(SCREEN_WIDTH)
This works, but when we we collect all the coins we are done. What if it was a never-ending set of coins? Instead of “killing” the coin, let’s reset it to the top of the screen.
def animate(self, delta_time):
""" Movement and game logic """
self.all_sprites_list.update()
hit_list = arcade.check_for_collision_with_list(self.player_sprite, self.coin_list)
for coin in hit_list:
self.score += 1
# Reset the coin to a random spot above the screen
coin.center_y = random.randrange(SCREEN_HEIGHT + 20,
SCREEN_HEIGHT + 100)
coin.center_x = random.randrange(SCREEN_WIDTH)
We can even take that common code, and move it to a method. Here’s a 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 | import random
import arcade
SPRITE_SCALING = 0.5
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class Coin(arcade.Sprite):
"""
This class represents the coins on our screen. It is a child class of
the arcade library's "Sprite" class.
"""
def reset_pos(self):
# Reset the coin to a random spot above the screen
self.center_y = random.randrange(SCREEN_HEIGHT + 20,
SCREEN_HEIGHT + 100)
self.center_x = random.randrange(SCREEN_WIDTH)
def update(self):
# Move the coin
self.center_y -= 1
# See if the coin has fallen off the bottom of the screen.
# If so, reset it.
if self.top < 0:
self.reset_pos()
class MyAppWindow(arcade.Window):
""" Main application class. """
def __init__(self, width, height):
"""
Initializer
:param width:
:param height:
"""
super().__init__(width, height)
# Sprite lists
self.all_sprites_list = None
self.coin_list = None
# Set up the player
self.score = 0
self.player_sprite = None
def start_new_game(self):
""" Set up the game and initialize the variables. """
# Sprite lists
self.all_sprites_list = arcade.SpriteList()
self.coin_list = arcade.SpriteList()
# Set up the player
self.score = 0
self.player_sprite = arcade.Sprite("character.png",
SPRITE_SCALING)
self.player_sprite.center_x = 50
self.player_sprite.center_y = 70
self.all_sprites_list.append(self.player_sprite)
for i in range(50):
# Create the coin instance
coin = Coin("coin_01.png", SPRITE_SCALING / 3)
# Position the coin
coin.center_x = random.randrange(SCREEN_WIDTH)
coin.center_y = random.randrange(SCREEN_HEIGHT)
# Add the coin to the lists
self.all_sprites_list.append(coin)
self.coin_list.append(coin)
# Don't show the mouse cursor
self.set_mouse_visible(False)
# 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.all_sprites_list.draw()
# Put the text on the screen.
output = "Score: {}".format(self.score)
arcade.draw_text(output, 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 animate(self, delta_time):
""" Movement and game logic """
# Call update on all sprites (The sprites don't do much in this
# example though.)
self.all_sprites_list.update()
# Generate a list of all sprites that collided with the player.
hit_list = arcade.check_for_collision_with_list(self.player_sprite,
self.coin_list)
# Loop through each colliding sprite, remove it, and add to the score.
for coin in hit_list:
coin.reset_pos()
self.score += 1
def main():
window = MyAppWindow(SCREEN_WIDTH, SCREEN_HEIGHT)
window.start_new_game()
arcade.run()
if __name__ == "__main__":
main()
|
11.2.2 Bouncing¶
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 | import random
import arcade
SPRITE_SCALING = 0.5
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class Coin(arcade.Sprite):
def __init__(self, filename, sprite_scaling):
super().__init__(filename, sprite_scaling)
self.change_x = 0
self.change_y = 0
def update(self):
# Move the coin
self.center_x += self.change_x
self.center_y += self.change_y
# If we are out-of-bounds, then 'bounce'
if self.left < 0:
self.change_x *= -1
if self.right > SCREEN_WIDTH:
self.change_x *= -1
if self.bottom < 0:
self.change_y *= -1
if self.top > SCREEN_HEIGHT:
self.change_y *= -1
class MyAppWindow(arcade.Window):
""" Main application class. """
def __init__(self, width, height):
super().__init__(width, height)
# Sprite lists
self.all_sprites_list = None
self.coin_list = None
# Set up the player
self.score = 0
self.player_sprite = None
def start_new_game(self):
""" Set up the game and initialize the variables. """
# Sprite lists
self.all_sprites_list = arcade.SpriteList()
self.coin_list = arcade.SpriteList()
# Set up the player
self.score = 0
self.player_sprite = arcade.Sprite("character.png",
SPRITE_SCALING)
self.player_sprite.center_x = 50
self.player_sprite.center_y = 70
self.all_sprites_list.append(self.player_sprite)
for i in range(50):
# Create the coin instance
coin = Coin("coin_01.png", SPRITE_SCALING / 3)
# Position the coin
coin.center_x = random.randrange(SCREEN_WIDTH)
coin.center_y = random.randrange(SCREEN_HEIGHT)
coin.change_x = random.randrange(-3, 4)
coin.change_y = random.randrange(-3, 4)
# Add the coin to the lists
self.all_sprites_list.append(coin)
self.coin_list.append(coin)
# Don't show the mouse cursor
self.set_mouse_visible(False)
# Set the background color
arcade.set_background_color(arcade.color.AMAZON)
def on_draw(self):
# This command has to happen before we start drawing
arcade.start_render()
# Draw all the sprites.
self.all_sprites_list.draw()
# Put the text on the screen.
output = "Score: " + str(self.score)
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)
def on_mouse_motion(self, x, y, dx, dy):
self.player_sprite.center_x = x
self.player_sprite.center_y = y
def animate(self, delta_time):
""" Movement and game logic """
# Call update on all sprites (The sprites don't do much in this
# example though.)
self.all_sprites_list.update()
# Generate a list of all sprites that collided with the player.
hit_list = arcade.check_for_collision_with_list(self.player_sprite,
self.coin_list)
# Loop through each colliding sprite, remove it, and add to the score.
for coin in hit_list:
self.score += 1
coin.kill()
def main():
window = MyAppWindow(SCREEN_WIDTH, SCREEN_HEIGHT)
window.start_new_game()
arcade.run()
if __name__ == "__main__":
main()
|
Take what you’ve learned from the example above, and see if you can replicate this:
11.2.3 Circles¶
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 | import random
import arcade
import math
SPRITE_SCALING = 0.5
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class Coin(arcade.Sprite):
def __init__(self, filename, sprite_scaling):
""" Constructor. """
# Call the parent class (Sprite) constructor
super().__init__(filename, sprite_scaling)
# Current angle in radians
self.angle = 0
# How far away from the center to orbit, in pixels
self.radius = 0
# How fast to orbit, in radians per frame
self.speed = 0.008
# Set the center of the point we will orbit around
self.circle_center_x = 0
self.circle_center_y = 0
def update(self):
""" Update the ball's position. """
# Calculate a new x, y
self.center_x = self.radius * math.sin(self.angle) \
+ self.circle_center_x
self.center_y = self.radius * math.cos(self.angle) \
+ self.circle_center_y
# Increase the angle in prep for the next round.
self.angle += self.speed
class MyAppWindow(arcade.Window):
""" Main application class. """
def __init__(self, width, height):
super().__init__(width, height)
# Sprite lists
self.all_sprites_list = None
self.coin_list = None
# Set up the player
self.score = 0
self.player_sprite = None
def start_new_game(self):
""" Set up the game and initialize the variables. """
# Sprite lists
self.all_sprites_list = arcade.SpriteList()
self.coin_list = arcade.SpriteList()
# Set up the player
self.score = 0
self.player_sprite = arcade.Sprite("character.png",
SPRITE_SCALING)
self.player_sprite.center_x = 50
self.player_sprite.center_y = 70
self.all_sprites_list.append(self.player_sprite)
for i in range(50):
# Create the coin instance
coin = Coin("coin_01.png", SPRITE_SCALING / 3)
# Position the center of the circle the coin will orbit
coin.circle_center_x = random.randrange(SCREEN_WIDTH)
coin.circle_center_y = random.randrange(SCREEN_HEIGHT)
# Random radius from 10 to 200
coin.radius = random.randrange(10, 200)
# Random start angle from 0 to 2pi
coin.angle = random.random() * 2 * math.pi
# Add the coin to the lists
self.all_sprites_list.append(coin)
self.coin_list.append(coin)
# Don't show the mouse cursor
self.set_mouse_visible(False)
# Set the background color
arcade.set_background_color(arcade.color.AMAZON)
def on_draw(self):
# This command has to happen before we start drawing
arcade.start_render()
# Draw all the sprites.
self.all_sprites_list.draw()
# Put the text on the screen.
output = "Score: " + str(self.score)
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)
def on_mouse_motion(self, x, y, dx, dy):
self.player_sprite.center_x = x
self.player_sprite.center_y = y
def animate(self, delta_time):
""" Movement and game logic """
# Call update on all sprites (The sprites don't do much in this
# example though.)
self.all_sprites_list.update()
# Generate a list of all sprites that collided with the player.
hit_list = arcade.check_for_collision_with_list(self.player_sprite,
self.coin_list)
# Loop through each colliding sprite, remove it, and add to the score.
for coin in hit_list:
self.score += 1
coin.kill()
def main():
window = MyAppWindow(SCREEN_WIDTH, SCREEN_HEIGHT)
window.start_new_game()
arcade.run()
if __name__ == "__main__":
main()
|
11.3 Rotating Sprites¶
Sprites can easily be rotated by setting their angle
attribute. Angles are
in degrees. So the following will flip the player upside down:
self.player_sprite.angle = 180
If you put this in the coin’s animate
method, it would cause the coins to
spin:
self.angle += 1
# If we rotate past 360, reset it back a turn.
if self.angle > 359:
self.angle -= 360
11.4 Using Sprites to Shoot¶
How do we get sprites that we can shoot?
First, we need a ‘shooting’ image:
To start with, we need a sprite to represent the bullet. It will be a moving sprite:
class Bullet(arcade.Sprite):
def update(self):
self.center_y += BULLET_SPEED
This gets the bullets to move up. But we don’t have any 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 = Bullet("laserBlue01.png", SPRITE_SCALING * 1.5)
# 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.all_sprites_list.append(bullet)
self.bullet_list.append(bullet)
The two key points with the code above is that 1.) We position the bullet right above the player that spawned it:
bullet.center_x = self.player_sprite.center_x
bullet.bottom = self.player_sprite.top
And two, we can rotate a sprite! Since the bullet image has the bullet going
sideways, that’s no good. There is an attribute with any sprite that you can
set called angle
. So we just set the angle to 90 to rotate it.
bullet.angle = 90
Now that we have bullets, how do we get them to collide with the coins?
We add the following to our applications animate
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.kill()
# For every coin we hit, add to the score and remove the coin
for coin in hit_list:
coin.kill()
self.score += 1
# If the bullet flies off-screen, remove it.
if bullet.bottom > SCREEN_HEIGHT:
bullet.kill()
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 | import random
import arcade
SPRITE_SCALING = 0.5
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
BULLET_SPEED = 5
class Bullet(arcade.Sprite):
def update(self):
self.center_y += BULLET_SPEED
class MyAppWindow(arcade.Window):
""" Main application class. """
def __init__(self):
super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprites and Bullets Demo")
""" Set up the game and initialize the variables. """
# Sprite lists
self.all_sprites_list = arcade.SpriteList()
self.coin_list = arcade.SpriteList()
self.bullet_list = arcade.SpriteList()
# Set up the player
self.score = 0
self.player_sprite = arcade.Sprite("character.png", SPRITE_SCALING)
self.player_sprite.center_x = 50
self.player_sprite.center_y = 70
self.all_sprites_list.append(self.player_sprite)
for i in range(50):
# Create the coin instance
coin = arcade.Sprite("coin_01.png", SPRITE_SCALING / 3)
# 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.all_sprites_list.append(coin)
self.coin_list.append(coin)
# Don't show the mouse cursor
self.set_mouse_visible(False)
# Set the background color
arcade.set_background_color(arcade.color.AMAZON)
def on_draw(self):
arcade.start_render()
# Draw all the sprites.
self.all_sprites_list.draw()
# Put the text on the screen.
output = "Score: " + str(self.score)
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)
def on_mouse_motion(self, x, y, dx, dy):
self.player_sprite.center_x = x
def on_mouse_press(self, x, y, button, modifiers):
# Create a bullet
bullet = Bullet("laserBlue01.png", SPRITE_SCALING * 1.5)
# 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.all_sprites_list.append(bullet)
self.bullet_list.append(bullet)
def animate(self, delta_time):
""" Movement and game logic """
# Call update on all sprites (The sprites don't do much in this
# example though.)
self.all_sprites_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.kill()
# For every coin we hit, add to the score and remove the coin
for coin in hit_list:
coin.kill()
self.score += 1
# If the bullet flies off-screen, remove it.
if bullet.bottom > SCREEN_HEIGHT:
bullet.kill()
def main():
MyAppWindow()
arcade.run()
if __name__ == "__main__":
main()
|