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()