25. Sprites and Walls
Many games with sprites often have “walls” that the character can’t move through. This can be for a top-down game where the player moves around obstacles, or a side-view platformer game.
We need to learn:
How to create and place the walls.
How to keep the user from moving through a wall.
How to “scroll” the screen so that we can have a large world to move through.
25.1. Setup
To begin with, let’s get a couple images. Our character, and a box that will act as a blocking wall. Both images are from kenney.nl.

images/character.png

images/boxCrate_double.png
Start with a default file:
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 | """ Sprite Sample Program """ import arcade # --- Constants --- SPRITE_SCALING_BOX = 0.5 SPRITE_SCALING_PLAYER = 0.5 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 MOVEMENT_SPEED = 5 class MyGame(arcade.Window): """ This class represents the main window of the game. """ def __init__(self): """ Initializer """ # Call the parent class initializer super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprites With Walls Example") def setup(self): # Set the background color arcade.set_background_color(arcade.color.AMAZON) def on_draw(self): self.clear() def main(): window = MyGame() window.setup() arcade.run() if __name__ == "__main__": main() |
In the __init__
method, let’s create some variables to hold our sprites:
# Sprite lists
self.player_list = None
self.wall_list = None
# Set up the player
self.player_sprite = None
# This variable holds our simple "physics engine"
self.physics_engine = None
In the setup
method, let’s create our sprite lists:
# Sprite lists
self.player_list = arcade.SpriteList()
self.wall_list = arcade.SpriteList()
Then reset the score and create the player:
# Reset the score
self.score = 0
# Create the player
self.player_sprite = arcade.Sprite("images/character.png", SPRITE_SCALING_PLAYER)
self.player_sprite.center_x = 50
self.player_sprite.center_y = 64
self.player_list.append(self.player_sprite)
Then go ahead and draw everything in our on_draw
:
def on_draw(self):
arcade.start_render()
self.wall_list.draw()
self.player_list.draw()
Run the program and make sure it works.

25.2. Individually Placing Walls
In our setup
method, we can position individual boxes to be used as “walls”:
# Manually create and position a box at 300, 200
wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX)
wall.center_x = 300
wall.center_y = 200
self.wall_list.append(wall)
# Manually create and position a box at 364, 200
wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX)
wall.center_x = 364
wall.center_y = 200
self.wall_list.append(wall)
Go ahead and try it out. It should look like:

Full listing below:
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 | """ Sprite Sample Program """ import arcade # --- Constants --- SPRITE_SCALING_BOX = 0.5 SPRITE_SCALING_PLAYER = 0.5 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 MOVEMENT_SPEED = 5 class MyGame(arcade.Window): """ This class represents the main window of the game. """ def __init__(self): """ Initializer """ # Call the parent class initializer super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprite Example") # Sprite lists self.player_list = None self.wall_list = None # Set up the player self.player_sprite = None def setup(self): # Set the background color arcade.set_background_color(arcade.color.AMAZON) # Sprite lists self.player_list = arcade.SpriteList() self.wall_list = arcade.SpriteList() # Reset the score self.score = 0 # Create the player self.player_sprite = arcade.Sprite("images/character.png", SPRITE_SCALING_PLAYER) self.player_sprite.center_x = 50 self.player_sprite.center_y = 64 self.player_list.append(self.player_sprite) # Manually create and position a box at 300, 200 wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = 300 wall.center_y = 200 self.wall_list.append(wall) # Manually create and position a box at 364, 200 wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = 364 wall.center_y = 200 self.wall_list.append(wall) def on_draw(self): self.clear() self.wall_list.draw() self.player_list.draw() def main(): """ Main method """ window = MyGame() window.setup() arcade.run() if __name__ == "__main__": main() |
25.3. Placing Walls With A Loop
In our setup
method, we can create a row of box sprites using a for
loop. In the code below, our y value is always 350, and we change the x value
from 173 to 650. We put a box every 64 pixels because each box happens to be
64 pixels wide.
# Place boxes inside a loop
for x in range(173, 650, 64):
wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX)
wall.center_x = x
wall.center_y = 350
self.wall_list.append(wall)

25.4. Placing Walls With A List
You could even create a list of coordinates, and then just loop through that list creating walls:
# --- Place walls with a list
coordinate_list = [[400, 500],
[470, 500],
[400, 570],
[470, 570]]
# Loop through coordinates
for coordinate in coordinate_list:
wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX)
wall.center_x = coordinate[0]
wall.center_y = coordinate[1]
self.wall_list.append(wall)

Full listing below:
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 | """ Sprite Sample Program """ import arcade # --- Constants --- SPRITE_SCALING_BOX = 0.5 SPRITE_SCALING_PLAYER = 0.5 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 MOVEMENT_SPEED = 5 class MyGame(arcade.Window): """ This class represents the main window of the game. """ def __init__(self): """ Initializer """ # Call the parent class initializer super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprite Example") # Sprite lists self.player_list = None self.wall_list = None # Set up the player self.player_sprite = None def setup(self): # Set the background color arcade.set_background_color(arcade.color.AMAZON) # Sprite lists self.player_list = arcade.SpriteList() self.wall_list = arcade.SpriteList() # Reset the score self.score = 0 # Create the player self.player_sprite = arcade.Sprite("images/character.png", SPRITE_SCALING_PLAYER) self.player_sprite.center_x = 50 self.player_sprite.center_y = 64 self.player_list.append(self.player_sprite) # --- Manually place walls # Manually create and position a box at 300, 200 wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = 300 wall.center_y = 200 self.wall_list.append(wall) # Manually create and position a box at 364, 200 wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = 364 wall.center_y = 200 self.wall_list.append(wall) # --- Place boxes inside a loop for x in range(173, 650, 64): wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = x wall.center_y = 350 self.wall_list.append(wall) # --- Place walls with a list coordinate_list = [[400, 500], [470, 500], [400, 570], [470, 570]] # Loop through coordinates for coordinate in coordinate_list: wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = coordinate[0] wall.center_y = coordinate[1] self.wall_list.append(wall) def on_draw(self): self.clear() self.player_list.draw() self.wall_list.draw() def main(): """ Main method """ window = MyGame() window.setup() arcade.run() if __name__ == "__main__": main() |
25.5. Physics Engine
First, we need to hook the keyboard up to the player:
def on_key_press(self, key, modifiers):
"""Called whenever a key is pressed. """
if key == arcade.key.UP:
self.player_sprite.change_y = MOVEMENT_SPEED
elif key == arcade.key.DOWN:
self.player_sprite.change_y = -MOVEMENT_SPEED
elif key == arcade.key.LEFT:
self.player_sprite.change_x = -MOVEMENT_SPEED
elif key == arcade.key.RIGHT:
self.player_sprite.change_x = MOVEMENT_SPEED
def on_key_release(self, key, modifiers):
"""Called when the user releases a key. """
if key == arcade.key.UP or key == arcade.key.DOWN:
self.player_sprite.change_y = 0
elif key == arcade.key.LEFT or key == arcade.key.RIGHT:
self.player_sprite.change_x = 0
Now, we need to add a way to stop the player from running into walls.
The Arcade Library has a built in “physics engine.” A physics engine handles the interactions between the virtual physical objects in the game. For example, a physics engine might be several balls running into each other, a character sliding down a hill, or a car making a turn on the road.
Physics engines have made impressive gains on what they can simulate. For our game, we’ll just keep things simple and make sure our character can’t walk through walls.
We’ll create variable to hold our physics engine in the __init__
:
# This variable holds our simple "physics engine"
self.physics_engine = None
We can create the actual physics engine in our setup
method with the following
code:
self.physics_engine = arcade.PhysicsEngineSimple(self.player_sprite, self.wall_list)
This identifies the player character (player_sprite
), and a list of sprites
(wall_list
) that the player character isn’t allowed to pass through.
Before, we updated all the sprites with a self.all_sprites_list.update()
command. With the physics engine, we will instead update the sprites by using
the physics engine’s update:
def update(self, delta_time):
self.physics_engine.update()
The simple physics engine follows the following algorithm:
Move the player in the x direction according to the player’s
change_x
value.Check the player against the wall list and see if there are any collisions.
If the player is colliding:
If the player is moving right, set the player’s right edge to the wall’s left edge.
If the player is moving left, set the player’s left edge to the wall’s right edge.
If the player isn’t moving left or right, print out a message that we are confused how we hit something when we aren’t moving.
Then we just do the same thing, except with the y coordinates.
You can see the physics engine source code on GitHub.
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 | """ Sprite Sample Program """ import arcade # --- Constants --- SPRITE_SCALING_BOX = 0.5 SPRITE_SCALING_PLAYER = 0.5 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 MOVEMENT_SPEED = 5 class MyGame(arcade.Window): """ This class represents the main window of the game. """ def __init__(self): """ Initializer """ # Call the parent class initializer super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprite Example") # Sprite lists self.player_list = None self.wall_list = None # Set up the player self.player_sprite = None # This variable holds our simple "physics engine" self.physics_engine = None def setup(self): # Set the background color arcade.set_background_color(arcade.color.AMAZON) # Sprite lists self.player_list = arcade.SpriteList() self.wall_list = arcade.SpriteList() # Reset the score self.score = 0 # Create the player self.player_sprite = arcade.Sprite("images/character.png", SPRITE_SCALING_PLAYER) self.player_sprite.center_x = 50 self.player_sprite.center_y = 64 self.player_list.append(self.player_sprite) # --- Manually place walls # Manually create and position a box at 300, 200 wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = 300 wall.center_y = 200 self.wall_list.append(wall) # Manually create and position a box at 364, 200 wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = 364 wall.center_y = 200 self.wall_list.append(wall) # --- Place boxes inside a loop for x in range(173, 650, 64): wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = x wall.center_y = 350 self.wall_list.append(wall) # --- Place walls with a list coordinate_list = [[400, 500], [470, 500], [400, 570], [470, 570]] # Loop through coordinates for coordinate in coordinate_list: wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = coordinate[0] wall.center_y = coordinate[1] self.wall_list.append(wall) # Create the physics engine. Give it a reference to the player, and # the walls we can't run into. self.physics_engine = arcade.PhysicsEngineSimple(self.player_sprite, self.wall_list) def on_draw(self): self.clear() self.wall_list.draw() self.player_list.draw() def on_update(self, delta_time): self.physics_engine.update() def on_key_press(self, key, modifiers): """Called whenever a key is pressed. """ if key == arcade.key.UP: self.player_sprite.change_y = MOVEMENT_SPEED elif key == arcade.key.DOWN: self.player_sprite.change_y = -MOVEMENT_SPEED elif key == arcade.key.LEFT: self.player_sprite.change_x = -MOVEMENT_SPEED elif key == arcade.key.RIGHT: self.player_sprite.change_x = MOVEMENT_SPEED def on_key_release(self, key, modifiers): """Called when the user releases a key. """ if key == arcade.key.UP or key == arcade.key.DOWN: self.player_sprite.change_y = 0 elif key == arcade.key.LEFT or key == arcade.key.RIGHT: self.player_sprite.change_x = 0 def main(): """ Main method """ window = MyGame() window.setup() arcade.run() if __name__ == "__main__": main() |
25.6. Using a Camera for Scrolling
What if one screen isn’t enough to hold your maze of walls? We can have a world that is larger than just our window. Then we simply move a camera that defines what part of the world we want to see.
Normally coordinate (0, 0) is the lower left corner of our screen. We can change that! We could have an entire world stretch from (0, 0) to (3000, 2000), and have a smaller camera view that is 800x600, which we scrolled around that large view.
There is also part of the view that we typically don’t want to have scroll around the screen. For example, we may want the score to show in the lower left corner of the screen. We want it fixed there no matter where our view into the world is. To do that, we’ll have not one, but two cameras. One for the larger world, and one for our score (or anything in the heads-up-display.)
To do this, let’s create two cameras in our Window class’s __init__
method.
Both of these cameras will have a field-of-view set to the same size as the window.
# Create the cameras. One for the GUI, one for the sprites.
# We scroll the 'sprite world' but not the GUI.
self.camera_for_sprites = arcade.Camera(SCREEN_WIDTH, SCREEN_HEIGHT)
self.camera_for_gui = arcade.Camera(SCREEN_WIDTH, SCREEN_HEIGHT)
Now we have cameras. We just need to use the cameras. In the Window’s draw
method,
select which camera we want to use before we draw.
def on_draw(self):
arcade.start_render()
# Select the scrolled camera for our sprites
self.camera_for_sprites.use()
# Draw the sprites
self.wall_list.draw()
self.player_list.draw()
# Select the (unscrolled) camera for our GUI
self.camera_for_gui.use()
arcade.draw_text(f"Score: {self.score}", 10, 10, arcade.color.WHITE, 24)
At this point the program should run, but since we haven’t moved the camera, it will still look the same.
To scroll the camera, we’ll add code to calculate the coordinates of the lower-left corner of our camera.
Then call the camera’s move_to
method to scroll there.
def on_update(self, delta_time):
""" Movement and game logic """
# Call update on all sprites (The sprites don't do much in this
# example though.)
self.physics_engine.update()
# Scroll the screen to the player
self.scroll_to_player()
# Scroll the window to the player.
#
# If CAMERA_SPEED is 1, the camera will immediately move to the desired position.
# Anything between 0 and 1 will have the camera move to the location with a smoother
# pan.
CAMERA_SPEED = 1
lower_left_corner = (self.player_sprite.center_x - self.width / 2,
self.player_sprite.center_y - self.height / 2)
self.camera_for_sprites.move_to(lower_left_corner, CAMERA_SPEED)
The full example is below:
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 148 149 150 151 152 153 154 155 156 157 | """ Sprite Sample Program """ import arcade # --- Constants --- SPRITE_SCALING_BOX = 0.5 SPRITE_SCALING_PLAYER = 0.5 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 MOVEMENT_SPEED = 5 class MyGame(arcade.Window): """ This class represents the main window of the game. """ def __init__(self): """ Initializer """ # Call the parent class initializer super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprite Example") # Sprite lists self.player_list = None self.wall_list = None # Set up the player self.player_sprite = None # This variable holds our simple "physics engine" self.physics_engine = None # Create the cameras. One for the GUI, one for the sprites. # We scroll the 'sprite world' but not the GUI. self.camera_for_sprites = arcade.Camera(SCREEN_WIDTH, SCREEN_HEIGHT) self.camera_for_gui = arcade.Camera(SCREEN_WIDTH, SCREEN_HEIGHT) def setup(self): # Set the background color arcade.set_background_color(arcade.color.AMAZON) # Sprite lists self.player_list = arcade.SpriteList() self.wall_list = arcade.SpriteList() # Reset the score self.score = 0 # Create the player self.player_sprite = arcade.Sprite("images/character.png", SPRITE_SCALING_PLAYER) self.player_sprite.center_x = 50 self.player_sprite.center_y = 64 self.player_list.append(self.player_sprite) # --- Manually place walls # Manually create and position a box at 300, 200 wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = 300 wall.center_y = 200 self.wall_list.append(wall) # Manually create and position a box at 364, 200 wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = 364 wall.center_y = 200 self.wall_list.append(wall) # --- Place boxes inside a loop for x in range(173, 650, 64): wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = x wall.center_y = 350 self.wall_list.append(wall) # --- Place walls with a list coordinate_list = [[400, 500], [470, 500], [400, 570], [470, 570]] # Loop through coordinates for coordinate in coordinate_list: wall = arcade.Sprite("images/boxCrate_double.png", SPRITE_SCALING_BOX) wall.center_x = coordinate[0] wall.center_y = coordinate[1] self.wall_list.append(wall) # Create the physics engine. Give it a reference to the player, and # the walls we can't run into. self.physics_engine = arcade.PhysicsEngineSimple(self.player_sprite, self.wall_list) def on_draw(self): self.clear() # Select the scrolled camera for our sprites self.camera_for_sprites.use() # Draw the sprites self.wall_list.draw() self.player_list.draw() # Select the (unscrolled) camera for our GUI self.camera_for_gui.use() arcade.draw_text(f"Score: {self.score}", 10, 10, arcade.color.WHITE, 24) def on_update(self, delta_time): """ Movement and game logic """ # Call update on all sprites (The sprites don't do much in this # example though.) self.physics_engine.update() # Scroll the screen to the player self.scroll_to_player() # Scroll the window to the player. # # If CAMERA_SPEED is 1, the camera will immediately move to the desired position. # Anything between 0 and 1 will have the camera move to the location with a smoother # pan. CAMERA_SPEED = 1 lower_left_corner = (self.player_sprite.center_x - self.width / 2, self.player_sprite.center_y - self.height / 2) self.camera_for_sprites.move_to(lower_left_corner, CAMERA_SPEED) def on_key_press(self, key, modifiers): """Called whenever a key is pressed. """ if key == arcade.key.UP: self.player_sprite.change_y = MOVEMENT_SPEED elif key == arcade.key.DOWN: self.player_sprite.change_y = -MOVEMENT_SPEED elif key == arcade.key.LEFT: self.player_sprite.change_x = -MOVEMENT_SPEED elif key == arcade.key.RIGHT: self.player_sprite.change_x = MOVEMENT_SPEED def on_key_release(self, key, modifiers): """Called when the user releases a key. """ if key == arcade.key.UP or key == arcade.key.DOWN: self.player_sprite.change_y = 0 elif key == arcade.key.LEFT or key == arcade.key.RIGHT: self.player_sprite.change_x = 0 def main(): """ Main method """ window = MyGame() window.setup() arcade.run() if __name__ == "__main__": main() |
25.7. Additional Examples
Another example is to just keep the user in a “box” and allow her to move inside that box without scrolling.
Instead of scrolling, you can also move between rooms: