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
9SCREEN_WIDTH = 800
10SCREEN_HEIGHT = 600
11SCREEN_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 MyGame(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__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_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(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png")
72
73 self.player_sprite = arcade.Sprite(self.player_texture)
74 self.player_sprite.center_x = 64
75 self.player_sprite.center_y = 128
76
77 self.player_list = arcade.SpriteList()
78 self.player_list.append(self.player_sprite)
79
80 self.wall_list = arcade.SpriteList(use_spatial_hash=True)
81 self.coin_list = arcade.SpriteList(use_spatial_hash=True)
82
83 # Create the ground
84 # This shows using a loop to place multiple sprites horizontally
85 for x in range(0, 1250, 64):
86 wall = arcade.Sprite(":resources:images/tiles/grassMid.png", scale=TILE_SCALING)
87 wall.center_x = x
88 wall.center_y = 32
89 self.wall_list.append(wall)
90
91 # Put some crates on the ground
92 # This shows using a coordinate list to place sprites
93 coordinate_list = [[512, 96], [256, 96], [768, 96]]
94
95 for coordinate in coordinate_list:
96 # Add a crate on the ground
97 wall = arcade.Sprite(
98 ":resources:images/tiles/boxCrate_double.png", scale=TILE_SCALING
99 )
100 wall.position = coordinate
101 self.wall_list.append(wall)
102
103 # Add coins to the world
104 for x in range(128, 1250, 256):
105 coin = arcade.Sprite(":resources:images/items/coinGold.png", scale=COIN_SCALING)
106 coin.center_x = x
107 coin.center_y = 96
108 self.coin_list.append(coin)
109
110 # Create a Platformer Physics Engine, this will handle moving our
111 # player as well as collisions between the player sprite and
112 # whatever SpriteList we specify for the walls.
113 # It is important to supply static to the walls parameter. There is a
114 # platforms parameter that is intended for moving platforms.
115 # If a platform is supposed to move, and is added to the walls list,
116 # it will not be moved.
117 self.physics_engine = arcade.PhysicsEnginePlatformer(
118 self.player_sprite, walls=self.wall_list, gravity_constant=GRAVITY
119 )
120
121 # Initialize our camera, setting a viewport the size of our window.
122 self.camera = arcade.camera.Camera2D()
123
124 # Initialize our gui camera, initial settings are the same as our world camera.
125 self.gui_camera = arcade.camera.Camera2D()
126
127 # Reset our score to 0
128 self.score = 0
129
130 # Initialize our arcade.Text object for score
131 self.score_text = arcade.Text(f"Score: {self.score}", x=0, y=5)
132
133 self.background_color = arcade.csscolor.CORNFLOWER_BLUE
134
135 def on_draw(self):
136 """Render the screen."""
137
138 # Clear the screen to the background color
139 self.clear()
140
141 # Activate our camera before drawing
142 self.camera.use()
143
144 # Draw our sprites
145 self.player_list.draw()
146 self.wall_list.draw()
147 self.coin_list.draw()
148
149 # Activate our GUI camera
150 self.gui_camera.use()
151
152 # Draw our Score
153 self.score_text.draw()
154
155 def on_update(self, delta_time):
156 """Movement and Game Logic"""
157
158 # Move the player using our physics engine
159 self.physics_engine.update()
160
161 # See if we hit any coins
162 coin_hit_list = arcade.check_for_collision_with_list(
163 self.player_sprite, self.coin_list
164 )
165
166 # Loop through each coin we hit (if any) and remove it
167 for coin in coin_hit_list:
168 # Remove the coin
169 coin.remove_from_sprite_lists()
170 arcade.play_sound(self.collect_coin_sound)
171 self.score += 75
172 self.score_text.text = f"Score: {self.score}"
173
174 # Center our camera on the player
175 self.camera.position = self.player_sprite.position
176
177 def on_key_press(self, key, modifiers):
178 """Called whenever a key is pressed."""
179
180 if key == arcade.key.ESCAPE:
181 self.setup()
182
183 if key == arcade.key.UP or key == arcade.key.W:
184 if self.physics_engine.can_jump():
185 self.player_sprite.change_y = PLAYER_JUMP_SPEED
186 arcade.play_sound(self.jump_sound)
187
188 if key == arcade.key.LEFT or key == arcade.key.A:
189 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
190 elif key == arcade.key.RIGHT or key == arcade.key.D:
191 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
192
193 def on_key_release(self, key, modifiers):
194 """Called whenever a key is released."""
195
196 if key == arcade.key.LEFT or key == arcade.key.A:
197 self.player_sprite.change_x = 0
198 elif key == arcade.key.RIGHT or key == arcade.key.D:
199 self.player_sprite.change_x = 0
200
201
202def main():
203 """Main function"""
204 window = MyGame()
205 window.setup()
206 arcade.run()
207
208
209if __name__ == "__main__":
210 main()
Run This Chapter¶
python -m arcade.examples.platform_tutorial.10_score