Typing Tutor Game

Game object - words

Now that we have our game loop, let's introduce our words to our game play display.

First, let's create our game objects class in a different script called objects.py, that initialises our word_list with our gutenberg words we have scraped.

# game objects.py import string import random class GameObjects: def __init__(self, screen_width, screen_height): self.screen_width = screen_width self.screen_height = screen_height self.word_list = self.init_words() def init_words(self): words_list = None try: with open("words.txt", 'r', newline='') as file: words_list = [line.strip() for line in file.readlines()] except: # Error handling, provide default: words_list = list(string.ascii_letters) random.shuffle(words_list) return words_list

Initialise words

And initialise our game objects at init of game play, in play.py:

# play.py
import pygame
from objects import GameObjects
# Colours R G B BLUE = ( 0, 0, 128) WHITE = (255, 255, 255) class GamePlay(): def __init__(self, screen): self.screen = screen pygame.init() self.disp_surf = pygame.Surface(self.screen.get_size()).convert() self.width, self.height = screen.get_size() self.font = pygame.font.Font('Consolas Bold.ttf', 16) self.playing = True
self.game_objects = GameObjects(self.width, self.height)

Adding words

We want to drop the words in regular intervals from top of the screen, and as such we need a clock.

And also drop immediately if there is no other words on screen.

First, we add to that our game words object, where we keep track of the current words at play:

class GameObjects:
    def __init__(self, screen_width, screen_height):
        self.screen_width = screen_width
        self.screen_height = screen_height
        self.word_list = self.init_words()
self.game_words = []

In the list of game words, we want to keep track of the words we have during game play. And so we create our singular word object:

# word.py class Word: def __init__(self, word): self.text = word

The length of the word is very important, so let's us set our __len__ special method.

We also want to set our x and y coordinate for our word on initialisation, the x coordinate will be fixed for the word through it's lifespan.

The initalised value of y will always be on the top of the screen. Let's drop from 20 pixels.

# word.py
# Dimensions STARTY = 20
class Word: def __init__(self, word, x): self.text = word
self._len = len(self.text) self._x = x self._y = STARTY def __len__(self): return self._len def get_x(self): return self._x def get_y(self): return self._y

Let's also add a method to return the top left coordinates. Since it would be required in order to draw the word.

    def get_y(self):
        return self._y
def coord(self): return self._x, self._y

To set the text of the word, we want to randomly select one from our word_list. So, we want to create a method to do so in our ojects.py, to add a word to our game word list, and additionally check that there is no duplicate first character in current word list.

# objects.py

def add_word(self): '''Add word to game play''' # Check that word doesn't duplicate first character in list word = random.choice(self.word_list) firstchar = [word.text[0] for word in self.game_words] while word[0] in firstchar: word = random.choice(self.word_list)

And we can calculate the X margins and font width and randomly select our word's x coordinate, which will be fixed for each word's life time :

# objects.py
import string
import random
from word import Word # Dimensions MARGINX = 5 FONTWIDTH = 9 # based on Consolas Bold 16 pt font size
    def add_word(self):
        '''Add word to game play'''
        # Check that word doesn't duplicate first character in list
        word = random.choice(self.word_list)
        firstchar = [word.text[0] for word in self.game_words]
        while word[0] in firstchar:
            word = random.choice(self.word_list)
        
# Calculate the max of x position of word max_x = (self.screen_width - MARGINX) - (FONTWIDTH * len(word)) # Some randomised fun x = random.randint(MARGINX,max_x) self.game_words.append(Word(word, x)) return True

Lastly, we want to control the timing we add the words.

So, let us add a timeout to our play.py

class GamePlay():
    def __init__(self, screen):
        self.screen = screen
        pygame.init()
        self.disp_surf = pygame.Surface(self.screen.get_size()).convert()
        self.width, self.height = screen.get_size()
        self.font =  pygame.font.Font('Consolas Bold.ttf', 16)
        self.playing = True
        self.game_objects = GameObjects(self.width, self.height)
        self.key_hit = None
self.last_add = 0 # time of adding word self.add_timeout = 2000 # 2 seconds

And we can create a wrapper method to check for the timeout before calling Objects.add_word()

# play.py
def add_word(self): # Check if valid to add if not self.game_objects.game_words or ( (pygame.time.get_ticks() - self.last_add) > self.add_timeout): if self.game_objects.add_word(): self.last_add = pygame.time.get_ticks() return

We can now add our game loop, with checking of events same as our main loop for now.

# play.py
    def loop(self):
        '''Game play loop'''
        self.check_key_hit()
self.add_word()
return

Draw words

Now we can easily call the method to draw our words onto the display screen and then update:

# play.py
    def on_render(self):
        '''Render surface'''
        self._display_surf.fill(BLUE)

# Draw play words for word in self.game_objects.game_words: self.draw_word(word.text, WHITE, word.coord())
# Blit everything to screen self.screen.blit(self.disp_surf, (0,0)) pygame.display.flip()