Step 3 - Many Sprites with SpriteList
So far our game is coming along nicely, we have a character on the screen! Wouldn’t it be nice if our character had a world to live in? In order to do that we’ll need to draw a lot more sprites. In this chapter we will explore SpriteList, a class Arcade provides to draw tons of Sprites at once.
At the end, we’ll have something like this:

SpriteList
arcade.SpriteList
exists to draw a collection of Sprites all at once. Let’s say for example that you have
100,000 box Sprites that you want to draw. Without SpriteList you would have to put all of your sprites into a list,
and then run a for loop over that which calls draw()
on every sprite.
This approach is extremely un-performant. Instead, you can add all of your boxes to a arcade.SpriteList
and then draw the SpriteList. Doing this, you are able to draw all 100,000 sprites for approximately the exact
same cost as drawing one sprite.
Note
This is due to Arcade being a heavily GPU based library. GPUs are really good at doing things in batches. This means we can send all the information about our sprites to the GPU, and then tell it to draw them all at once. However if we just draw one sprite at a time, then we have to go on a round trip from our CPU to our GPU every time.
Even if you are only drawing one Sprite, you should still create a SpriteList for it. Outside of small debugging
it is never better to draw an individual Sprite than it is to add it to a SpriteList. In fact, calling draw()
on a Sprite just creates a SpriteList internally to draw that Sprite with.
Let’s go ahead and create one for our player inside our __init__
function, and add the player to it.
self.player_list = arcade.SpriteList()
self.player_list.append(self.player_sprite)
Then in our on_draw
function, we can draw the SpriteList for the character instead of drawing the Sprite directly:
self.player_list.draw()
Now let’s try and build a world for our character. To do this, we’ll create a new SpriteList for the objects we’ll draw,
we can do this in our __init__
function.
self.wall_list = arcade.SpriteList(use_spatial_hash=True)
There’s a little bit to unpack in this snippet of code. Let’s address each issue:
Why not just use the same SpriteList we used for our player, and why is it named walls?
Eventually we will want to do collision detection between our character and these objects. In addition to drawing, SpriteLists also serve as a utility for collision detection. You can for example check for collisions between two SpriteLists, or pass SpriteLists into several physics engines. We will explore these topics in later chapters.
What is
use_spatial_hash
?This is also for collision detection. Spatial Hashing is a special algorithm which will make it much more performant, at the cost of being more expensive to move sprites. You will often see this option enabled on SpriteLists which are not expected to move much, such as walls or a floor.
With our newly created SpriteList, let’s go ahead and add some objects to it. We can add these lines to our
__init__
function.
for x in range(0, 1250, 64):
wall = arcade.Sprite(":resources:images/tiles/grassMid.png", TILE_SCALING)
wall.center_x = x
wall.center_y = 32
self.wall_list.append(wall)
coordinate_list = [[512, 96], [256, 96], [768, 96]]
for coordinate in coordinate_list:
wall = arcade.Sprite(
":resources:images/tiles/boxCrate_double.png", scale=0.5
)
wall.position = coordinate
self.wall_list.append(wall)
In these lines, we’re adding some grass and some crates to our SpriteList.
For the ground we’re using Python’s range
function to iterate on a list of X positions, which will give us
a horizontal line of Sprites. For the boxes, we’re inserting them at specified coordinates from a list.
We’re also doing a few new things in the arcade.Sprite
creation. First off we are passing the image file
directly instead of creating a texture first. This is ultimately doing the same thing, we’re just not managing the
texture ourselves, and letting Arcade handle it. We are also adding a scale to these sprites. For fun you can remove
the scale, and see how the images will be much larger.
Finally all we need to do in order to draw our new world, is draw the SpriteList for walls in on_draw
:
self.wall_list.draw()
Source Code
1"""
2Platformer Game
3
4python -m arcade.examples.platform_tutorial.03_more_sprites
5"""
6import arcade
7
8# Constants
9WINDOW_WIDTH = 1280
10WINDOW_HEIGHT = 720
11WINDOW_TITLE = "Platformer"
12
13# Constants used to scale our sprites from their original size
14TILE_SCALING = 0.5
15
16
17class GameView(arcade.Window):
18 """
19 Main application class.
20 """
21
22 def __init__(self):
23
24 # Call the parent class and set up the window
25 super().__init__(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
26
27 # Variable to hold our texture for our player
28 self.player_texture = arcade.load_texture(
29 ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"
30 )
31
32 # Separate variable that holds the player sprite
33 self.player_sprite = arcade.Sprite(self.player_texture)
34 self.player_sprite.center_x = 64
35 self.player_sprite.center_y = 128
36
37 # SpriteList for our player
38 self.player_list = arcade.SpriteList()
39 self.player_list.append(self.player_sprite)
40
41 # SpriteList for our boxes and ground
42 # Putting our ground and box Sprites in the same SpriteList
43 # will make it easier to perform collision detection against
44 # them later on. Setting the spatial hash to True will make
45 # collision detection much faster if the objects in this
46 # SpriteList do not move.
47 self.wall_list = arcade.SpriteList(use_spatial_hash=True)
48
49 # Create the ground
50 # This shows using a loop to place multiple sprites horizontally
51 for x in range(0, 1250, 64):
52 wall = arcade.Sprite(":resources:images/tiles/grassMid.png", scale=0.5)
53 wall.center_x = x
54 wall.center_y = 32
55 self.wall_list.append(wall)
56
57 # Put some crates on the ground
58 # This shows using a coordinate list to place sprites
59 coordinate_list = [[512, 96], [256, 96], [768, 96]]
60
61 for coordinate in coordinate_list:
62 # Add a crate on the ground
63 wall = arcade.Sprite(
64 ":resources:images/tiles/boxCrate_double.png", scale=0.5
65 )
66 wall.position = coordinate
67 self.wall_list.append(wall)
68
69 self.background_color = arcade.csscolor.CORNFLOWER_BLUE
70
71 def setup(self):
72 """Set up the game here. Call this function to restart the game."""
73 pass
74
75 def on_draw(self):
76 """Render the screen."""
77
78 # Clear the screen to the background color
79 self.clear()
80
81 # Draw our sprites
82 self.player_list.draw()
83 self.wall_list.draw()
84
85
86def main():
87 """Main function"""
88 window = GameView()
89 window.setup()
90 arcade.run()
91
92
93if __name__ == "__main__":
94 main()
Documentation for the
arcade.SpriteList
class
Note
Once you have the code up and working, try-out the following:
See if you can change the colors of all the boxes and ground using the SpriteList
Try and make a SpriteList invisible
Run This Chapter
python -m arcade.examples.platform_tutorial.03_more_sprites