Step 10 - Adding a Score
Our game is starting to take shape, but we still need to give the player a reward for their hard work collecting coins. To do this we will add a score which will be increased everytime they collect a coin, and display that on the screen.
In this chapter we will cover using arcade.Text
objects, as well as a technique for using two cameras
to draw objects in “screen space” and objects in “world space”.
Note
What is screen space and world space? Think about other games you may have played, and let’s compare it to our game. A player moves around in the world, and we scroll a camera around based on that position. This is an example of “world space” coordinates. They can expand beyond our window and need to be positioned within the window accordingly.
An example of “screen space” coordinates is our score indicator. We will draw this on our screen, but we don’t want it to move around the screen when the camera scrolls around. To achieve this we will use two different cameras, and move the world space camera, but not move the screen space camera.
In our code, we will call this screen space camera, gui_camera
Let’s go ahead and add a variable for our new camera and initialize it in setup
. We will also add a variable for
our score. This will just be an integer initially set to 0. We will set this in both __init__
and setup
.
# Within __init__
self.gui_camera = None
self.score = 0
# Within setup
self.gui_camera = arcade.SimpleCamera(viewport=(0, 0, width, height))
self.score = 0
Now we can go into our on_update
function, and when the player collects a coin, we can increment our score variable.
For now we will give the player 75 points for collecting a coin. You can change this, or as an exercise try adding different
types of coins with different point values. In later chapters we’ll explore dynamically providing point values for coins from
a map editor.
# Within on_update
for coin in coin_hit_list:
coin.remove_from_sprite_lists()
arcade.play_sound(self.collect_coin_sound)
self.score += 75
Now that we’re incrementing our score, how do we draw it onto the screen? Well we will be using our GUI camera, but so far we haven’t
talked about drawing Text in Arcade. There are a couple of ways we can do this in Arcade, the first way is using the
arcade.draw_text()
function. This is a simple function that you can put directly in on_draw
to draw a string of text.
This function however, is not very performant, and there is a better way. We will instead use arcade.Text
objects. These have many
advantages, like not needing to re-calculate the text everytime it’s drawn, and also can be batch drawn much like how we do with Sprite and SpriteList.
We will explore batch drawing Text later.
For now, let’s create an arcade.Text
object to hold our score text. First create the empty variable in __init__
and initialize in setup
.
# Within __init__
self.score_text = None
# Within setup
self.score_text = arcade.Text(f"Score: {self.score}", start_x = 0, start_y = 5)
The first parameter we send to arcade.Text
is a String containing the text we want to draw. In our example we provide an f-string which
adds our value from self.score
into the text. The other parameters are defining the bottom left point that our text will be drawn at.
I’ve set it to draw in the bottom left of our screen here. You can try moving it around.
Now we need to add this to our on_draw
function in order to get it to display on the screen.
# Within on_draw
self.gui_camera.use()
self.score_text.draw()
This will now draw our text in the bottom left of the screen. However, we stil have one problem left, we’re not updating the text when our user
gets a new score. In order to do this we will go back to our on_update
function, where we incremented the score when the user collects a coin,
and add one more line to it:
for coin in coin_hit_list:
coin.remove_from_sprite_lists()
arcade.play_sound(self.collect_coin_sound)
self.score += 75
self.score_text.text = f"Score: {self.score}"
In this new line we’re udpating the actual text of our Text object to contain the new score value.
Source Code
1"""
2Platformer Game
3
4python -m arcade.examples.platform_tutorial.10_score
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
15COIN_SCALING = 0.5
16
17# Movement speed of player, in pixels per frame
18PLAYER_MOVEMENT_SPEED = 5
19GRAVITY = 1
20PLAYER_JUMP_SPEED = 20
21
22
23class GameView(arcade.Window):
24 """
25 Main application class.
26 """
27
28 def __init__(self):
29
30 # Call the parent class and set up the window
31 super().__init__(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
32
33 # Variable to hold our texture for our player
34 self.player_texture = None
35
36 # Separate variable that holds the player sprite
37 self.player_sprite = None
38
39 # SpriteList for our player
40 self.player_list = None
41
42 # SpriteList for our boxes and ground
43 # Putting our ground and box Sprites in the same SpriteList
44 # will make it easier to perform collision detection against
45 # them later on. Setting the spatial hash to True will make
46 # collision detection much faster if the objects in this
47 # SpriteList do not move.
48 self.wall_list = None
49
50 # SpriteList for coins the player can collect
51 self.coin_list = None
52
53 # A variable to store our camera object
54 self.camera = None
55
56 # A variable to store our gui camera object
57 self.gui_camera = None
58
59 # This variable will store our score as an integer.
60 self.score = 0
61
62 # This variable will store the text for score that we will draw to the screen.
63 self.score_text = None
64
65 # Load sounds
66 self.collect_coin_sound = arcade.load_sound(":resources:sounds/coin1.wav")
67 self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")
68
69 def setup(self):
70 """Set up the game here. Call this function to restart the game."""
71 self.player_texture = arcade.load_texture(
72 ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"
73 )
74
75 self.player_sprite = arcade.Sprite(self.player_texture)
76 self.player_sprite.center_x = 64
77 self.player_sprite.center_y = 128
78
79 self.player_list = arcade.SpriteList()
80 self.player_list.append(self.player_sprite)
81
82 self.wall_list = arcade.SpriteList(use_spatial_hash=True)
83 self.coin_list = arcade.SpriteList(use_spatial_hash=True)
84
85 # Create the ground
86 # This shows using a loop to place multiple sprites horizontally
87 for x in range(0, 1250, 64):
88 wall = arcade.Sprite(":resources:images/tiles/grassMid.png", scale=TILE_SCALING)
89 wall.center_x = x
90 wall.center_y = 32
91 self.wall_list.append(wall)
92
93 # Put some crates on the ground
94 # This shows using a coordinate list to place sprites
95 coordinate_list = [[512, 96], [256, 96], [768, 96]]
96
97 for coordinate in coordinate_list:
98 # Add a crate on the ground
99 wall = arcade.Sprite(
100 ":resources:images/tiles/boxCrate_double.png", scale=TILE_SCALING
101 )
102 wall.position = coordinate
103 self.wall_list.append(wall)
104
105 # Add coins to the world
106 for x in range(128, 1250, 256):
107 coin = arcade.Sprite(":resources:images/items/coinGold.png", scale=COIN_SCALING)
108 coin.center_x = x
109 coin.center_y = 96
110 self.coin_list.append(coin)
111
112 # Create a Platformer Physics Engine, this will handle moving our
113 # player as well as collisions between the player sprite and
114 # whatever SpriteList we specify for the walls.
115 # It is important to supply static to the walls parameter. There is a
116 # platforms parameter that is intended for moving platforms.
117 # If a platform is supposed to move, and is added to the walls list,
118 # it will not be moved.
119 self.physics_engine = arcade.PhysicsEnginePlatformer(
120 self.player_sprite, walls=self.wall_list, gravity_constant=GRAVITY
121 )
122
123 # Initialize our camera, setting a viewport the size of our window.
124 self.camera = arcade.camera.Camera2D()
125
126 # Initialize our gui camera, initial settings are the same as our world camera.
127 self.gui_camera = arcade.camera.Camera2D()
128
129 # Reset our score to 0
130 self.score = 0
131
132 # Initialize our arcade.Text object for score
133 self.score_text = arcade.Text(f"Score: {self.score}", x=0, y=5)
134
135 self.background_color = arcade.csscolor.CORNFLOWER_BLUE
136
137 def on_draw(self):
138 """Render the screen."""
139
140 # Clear the screen to the background color
141 self.clear()
142
143 # Activate our camera before drawing
144 self.camera.use()
145
146 # Draw our sprites
147 self.player_list.draw()
148 self.wall_list.draw()
149 self.coin_list.draw()
150
151 # Activate our GUI camera
152 self.gui_camera.use()
153
154 # Draw our Score
155 self.score_text.draw()
156
157 def on_update(self, delta_time):
158 """Movement and Game Logic"""
159
160 # Move the player using our physics engine
161 self.physics_engine.update()
162
163 # See if we hit any coins
164 coin_hit_list = arcade.check_for_collision_with_list(
165 self.player_sprite, self.coin_list
166 )
167
168 # Loop through each coin we hit (if any) and remove it
169 for coin in coin_hit_list:
170 # Remove the coin
171 coin.remove_from_sprite_lists()
172 arcade.play_sound(self.collect_coin_sound)
173 self.score += 75
174 self.score_text.text = f"Score: {self.score}"
175
176 # Center our camera on the player
177 self.camera.position = self.player_sprite.position
178
179 def on_key_press(self, key, modifiers):
180 """Called whenever a key is pressed."""
181
182 if key == arcade.key.ESCAPE:
183 self.setup()
184
185 if key == arcade.key.UP or key == arcade.key.W:
186 if self.physics_engine.can_jump():
187 self.player_sprite.change_y = PLAYER_JUMP_SPEED
188 arcade.play_sound(self.jump_sound)
189
190 if key == arcade.key.LEFT or key == arcade.key.A:
191 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
192 elif key == arcade.key.RIGHT or key == arcade.key.D:
193 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
194
195 def on_key_release(self, key, modifiers):
196 """Called whenever a key is released."""
197
198 if key == arcade.key.LEFT or key == arcade.key.A:
199 self.player_sprite.change_x = 0
200 elif key == arcade.key.RIGHT or key == arcade.key.D:
201 self.player_sprite.change_x = 0
202
203
204def main():
205 """Main function"""
206 window = GameView()
207 window.setup()
208 arcade.run()
209
210
211if __name__ == "__main__":
212 main()
Run This Chapter
python -m arcade.examples.platform_tutorial.10_score