Typing Tutor Game
Typing ...
Now that we have our words dropping from screen, let's add the capability to read user inputs.
Just need to adjust our check event method from game.py into play.py to read TEXTINPUT
:
# play.py
def check_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
elif event.type == pygame.TEXTINPUT:
self.key_hit = event.text
elif event.type == pygame.KEYUP:
if event.key == pygame.K_ESCAPE:
self.playing = False
return
Check words
We also need a flag to indicate that we need to check for either all first characters of game words, for just check for the next character in the word we are typing:
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
self.fps = 25
self.fps_clock = pygame.time.Clock()
self.y_delta = 1
self.typing_flag = False
self.key_hit = None
self.score = 0
We use the typed_idx flag to indicate the word we are typing. Let's create a method to check our key_hit:
def check_key_hit(self):
'''Check user's typed key'''
if not self.key_hit:
return
if self.typing_flag:
for word in self.game_objects.game_words:
if word.typed_idx != -1:
if word.text[word.typed_idx+1] == self.key_hit:
if word.typed():
self.typing_flag = False
self.add_score(len(word))
else:
word.typed_reset()
self.typing_flag = False
else:
for word in self.game_objects.game_words:
if word.text[0] == self.key_hit:
if word.typed():
self.add_score(1)
else:
self.typing_flag = True
break
# Remove words set for removal
self.game_objects.clean_up()
self.key_hit = None
return
and an additional member to keep track fo the number of characters we typed, which can give us the score:
def add_score(self, score):
self.score += score
Let's add two more methods to our Word()
object, word.typed()
:
# word.py
def typed(self):
'''Returns True if completed typing word, False otherwise.'''
self.typed_idx += 1
if self.typed_idx >= self._len - 1:
# completed typing word
self.typed_idx = -2
return True
return False
and word.typed_reset()
to reset the typed_idx if player typed wrongly:
def typed_reset(self):
'''User typed incorrectly, reset flag'''
self.typed_idx = -1
return
We need to remember to clear the flag if the word we are typing drops below boundary_y. let's add a return flag to indicate we removed a word we are typing in our move method:
# objects.py
def move(self, delta):
'''Returns Flags if word is removed and if it is in the middle of typing,
when it has moved beyond boundary Y
'''
removed = False
removed_typing = False
for word in self.game_words:
word.move(delta)
if word.get_y() >= self.bound_y:
removed = True
if word.typed_idx >= 0:
removed_typing = True
word.set_remove()
self.clean_up()
return removed, removed_typing
Such that we can check the flag returned in play.py:
# play.py
def loop(self):
'''Game loop'''
self.check_key_hit()
# If moved beyond boundary Y
removed, removed_typing = self.game_objects.move(self.y_delta)
if removed_typing:
self.typing_flag = False
self.add_word()
return
Score
Now that we have our score, let's display our score on the top of our screen during play:
# play.py
import pygame
from objects import GameObjects
# Colours R G B
BLUE = ( 0, 0, 128)
WHITE = (255, 255, 255)
GREEN = ( 0, 200, 0)
BLACK = ( 0, 0, 0)
YELLOW = (255, 255, 0)
.
.
.
def draw_score(self):
# draw status bar background
pygame.draw.rect(self.disp_surf, BLACK, (0,0,self.width,20), 0)
# draw score text
text = 'Score: %s' % self.score
self.draw_word(text, YELLOW, (self.width - 150, 2))
return
Add to our game play's render():
def render(self):
'''Render surface'''
self.disp_surf.fill(BLUE) # Fill background
self.draw_score()
.
.
.
And display the score on GameOver:
# game.py
def render(self):
'''Render surface'''
self.disp_surf.fill(BLACK)
# set the text for display
if self.state == START:
text = 'Typing Tutorial Game'
message = 'Press space to play.'
else:
text = 'Game Over'
message = 'Score: %s' % self.game.score
# draw the messages
self.draw_text(text, GREEN, (int(WIDTH / 2), int(HEIGHT / 2) - 50))
self.draw_text(message, GREEN, (int(WIDTH / 2), int(HEIGHT / 2) + 50))
# Blit everything to screen
self.screen.blit(self.disp_surf, (0,0))
pygame.display.flip()
return
Draw typing characters
To indicate typing, we need to change the characters we typed into a different colour. We shall change it to green colour.
To have different colour, when we draw, we need to draw two rectangles, one with typed characters in green, and one replacing the typed characters with spaces and the rest of untyped characters in white. Which is why it is important we chose a font type with fixed width characters.
Let's adjust our on_render() method
# play.py
def render(self):
'''Render surface'''
self.disp_surf.fill(BLUE) # Fill background
self.draw_score()
# set the words for display
for word in self.game_objects.game_words:
if word.typed_idx > -1:
typed_text = word.text[:word.typed_idx+1]
untyped_text = ' '*(word.typed_idx+1) + word.text[word.typed_idx+1:]
self.draw_word(untyped_text, WHITE, word.coord())
self.draw_word(typed_text, GREEN, word.coord())
else:
self.draw_word(word.text, WHITE, word.coord())
# Blit everything to screen
self.screen.blit(self.disp_surf, (0,0))
pygame.display.flip()
return