Skip to content

13. Scoring

In this lesson we will learn

  • how to create a TextObject to show text on the screen
  • how to use and change the variables in Globals.py
  • how to change the score when events happen
  • how to test several changes with a testing table

Terminology

  • TextObject – a special kind of GameFrame RoomObject that displays text on the screen.
  • HUD – short for heads-up display, the information such as score and lives that is shown on the screen while the game is played.
  • testing table – a table for recording tests, with columns for the test, the expected result, the actual result and the remedy.

Now we have all our moving parts, it's time to reward the player for their efforts, and what better reward than a score?

GameFrame is event-driven, so the easiest way to add scoring is to connect it to events. We'll give the player both positive and negative scoring events:

  • rescuing an astronaut → +50 points
  • shooting an asteroid → +5 points
  • shooting an astronaut → −10 points

Let's work out how to add this to our code.

Display the score

Planning

First we need somewhere to keep the score. The Globals variables include SCORE, so we can use that.

Next we need a way to draw the score on the screen. The Text Object is a special kind of RoomObject that displays text, so we can treat it like any other RoomObject. Its __init__ method takes some extra arguments, and it has an update_text method that redraws the text.

So we have a way to record the score and a way to display it. Before we start changing the score, let's display the current SCORE on the screen.

Create the Score class

Create a new file in the Objects folder, add the code below and save it as Hud.py.

Objects/Hud.py
from GameFrame import TextObject, Globals

class Score(TextObject):
    """
    A class for displaying the current score
    """
    def __init__(self, room, x: int, y: int, text=None):
        """
        Initialises the score object
        """         
        # include attributes and methods from TextObject
        TextObject.__init__(self, room, x, y, text)

        # set values         
        self.size = 60
        self.font = 'Arial Black'
        self.colour = (255,255,255)
        self.bold = False
        self.update_text()
Code explanation
  • line 1 → imports TextObject (the parent class) and Globals (where SCORE lives).
  • line 3 → defines the Score class as a subclass of TextObject.
  • lines 4–6 → a docstring that explains what the class is for.
  • line 7 → defines the __init__ method, which takes the Room, the x and y position, and the text to display.
  • lines 8–10 → a docstring that explains what the method does.
  • line 12 → runs TextObject's __init__ method with the same four values.
  • lines 15–18 → set the font size, font, colour (white, as red, green and blue values) and whether the text is bold.
  • line 19 → draws the text with these settings. Without this call, nothing appears on the screen.

The file name and class name can be different

For the first time, the file name (Hud.py) and the class name (Score) are different. They don't have to match. We'll have two HUD (heads-up display) items, the score and the lives, so it makes sense to keep both classes in one file. We could have made Score.py and Lives.py instead. In coding there are often many good ways to reach the same result.

Open Objects/__init__.py, add the highlighted code below and save it.

Objects/__init__.py
1
2
3
4
5
6
7
from Objects.Title import Title
from Objects.Ship import Ship
from Objects.Zork import Zork
from Objects.Asteroid import Asteroid
from Objects.Laser import Laser
from Objects.Astronaut import Astronaut
from Objects.Hud import Score
Code explanation
  • line 7 → imports the Score class from Hud.py, so GameFrame can find it.

Add the score to the Room

Now we need to add a Score to the GamePlay Room. Open Rooms/GamePlay.py, add the highlighted code below and save it.

Rooms/GamePlay.py
from GameFrame import Level, Globals
from Objects.Ship import Ship
from Objects.Zork import Zork
from Objects.Hud import Score

class GamePlay(Level):
    def __init__(self, screen, joysticks):
        Level.__init__(self, screen, joysticks)

        # set background image
        self.set_background_image("background.png")

        # add objects
        self.add_room_object(Ship(self, 25, 50))
        self.add_room_object(Zork(self,1120, 50))

        # add HUD items
        self.score = Score(self, 
                           Globals.SCREEN_WIDTH/2 - 20, 20, 
                           str(Globals.SCORE))
        self.add_room_object(self.score)
Code explanation
  • line 1 → imports Globals as well as Level, so we can use SCORE and the screen width.
  • line 4 → imports the Score class.
  • lines 18–20 → create a Score near the top centre of the screen, showing the current SCORE as a string. We store it in self.score so other objects can find it later.
  • line 21 → adds the Score to the Room.

PRIMM

  1. Predict what you'll see at the top of the screen.
  2. Run MainController.py.
  3. Investigate: why do we use str(Globals.SCORE) instead of Globals.SCORE?

Change the score

We have a score on the screen. Now we need a way to change it. Several different collisions change the score, so the easiest way is a method that any object can call to update it.

Planning

The method needs to do two things:

  1. change the value of Globals.SCORE
  2. write the new score on the screen

Score update IPO

Coding

Open Objects/Hud.py and add the code below to the end of the Score class, then save it.

Objects/Hud.py
    def update_score(self, change):
        """
        Updates the score and redraws the text
        """
        Globals.SCORE += change
        self.text = str(Globals.SCORE)
        self.update_text()
Code explanation
  • line 21 → defines the update_score method, which takes change, the number of points to add.
  • lines 22–24 → a docstring that explains what the method does.
  • line 25 → adds change to Globals.SCORE (a negative change takes points away).
  • line 26 → sets the Score's text to the new score, as a string.
  • line 27 → redraws the text on the screen.

Run MainController.py to check there are no errors.


Add scores to collisions

Our plan has three collisions that change the score:

  1. Laser → Asteroid
  2. Laser → Astronaut
  3. Astronaut → Ship

We already have event handlers for all three, so we just need to add to them. Let's start with the Laser.

Open Objects/Laser.py, add the highlighted code below to the handle_collision method and save it.

Objects/Laser.py
    # --- Event handlers
    def handle_collision(self, other, other_type):
        """
        Handles laser collisions with other registered objects
        """
        if other_type == "Asteroid":
            self.room.delete_object(other)
            self.room.score.update_score(5)
        elif other_type == "Astronaut":
            self.room.delete_object(other)
            self.room.score.update_score(-10)
Code explanation
  • line 46 → when a laser shoots an asteroid, adds 5 to the score. self.room.score is the Score object we stored in the GamePlay Room.
  • line 49 → when a laser shoots an astronaut, takes 10 off the score.

Now open Objects/Astronaut.py, add the highlighted code below to the handle_collision method and save it.

Objects/Astronaut.py
    # --- Event Handlers
    def handle_collision(self, other, other_type):
        """
        Handles the collision event for Astronaut objects
        """
        # ship collision
        if other_type == "Ship":
            self.room.delete_object(self)
            self.room.score.update_score(50)
Code explanation
  • line 39 → when the ship rescues an astronaut, adds 50 to the score.

Testing

We've made three changes, so we need to test all three. We'll use a testing table with four columns:

  • Test: what we're testing
  • Expected result: what we expect to happen
  • Actual result: what actually happened when we tested
  • Remedy: if the actual result was different, how we fixed it

Here's our testing table with the first two columns filled in. Copy it, then run the game and finish it off.

Test Expected result Actual result Remedy
Laser shoots asteroid score + 5
Laser shoots astronaut score − 10
Ship rescues astronaut score + 50

Commit and push

  1. In GitHub Desktop, type Added scoring in the Summary box.
  2. Click Commit to main.
  3. Click Push origin.