Tutorial - Race game
In this chapter we will build a racing game together, step by step. The Python we will use is: conditionals, loops, lists, functions and tuples. We will show use of velocity, high score and a title screen.
Similar to the shooter game, we will begin with a complete program
listing but with empty bodies for some of the functions that we will
fill in later. (Python will not run a program with completely empty
functions, so they just contain
pass to indicate to Python they do
Like the shooter program, we begin we three things:
- Definitions of global variables.
These functions now check a boolean variable
instead of drawing/updating the game we show the title screen.
The only really complicated part of this program is how we store the
shape of the tunnel the player is racing down.
lines is a list of
tuples. A tuple is like a list but cannot be modified and can be
unpacked into separate variables. Each tuple will represent one
horizontal line of the screen. It will have three values,
color, representing the position of the left wall, the gap between the
left wall and the right wall and the colour of the wall.
import random import math WIDTH = 600 HEIGHT = 600 player = Actor("alien", (300, 580)) player.vx = 0 # horizontal velocity player.vy = 1 # vertical velocity lines =  # list of tuples of horizontal lines of walls wall_gradient = -3 # steepness of wall left_wall_x = 200 # x-coordinate of wall distance = 0 # how far player has travelled time = 15 # time left until game ends playing = False # True when in game, False when on title screen best_distance = 0 # remember the highest distance scored def draw(): screen.clear() if playing: # we are in game for i in range(0, len(lines)): # draw the walls x, x2, color = lines[i] screen.draw.line((0, i), (x, i), color) screen.draw.line((x + x2, i), (WIDTH, i), color) player.draw() else: # we are on title screen screen.draw.text("PRESS SPACE TO START", (150, 300),color="green",fontsize=40) screen.draw.text("BEST DISTANCE: "+str(int(best_distance / 10)), (170, 400), color="green", fontsize=40) screen.draw.text("SPEED: " + str(int(player.vy)), (0, 0), color="green", fontsize=40) screen.draw.text("DISTANCE: " + str(int(distance / 10)), (200, 0), color="green", fontsize=40) screen.draw.text("TIME: " + str(int(time)), (480, 0), color="green", fontsize=40) def update(delta): global playing, distance, time if playing: wall_collisions() scroll_walls() generate_lines() player_input() timer(delta) elif keyboard.space: playing = True distance = 0 time = 10 def player_input(): pass def generate_lines(): pass generate_lines() def scroll_walls(): pass def wall_collisions(): pass def timer(delta): pass def on_mouse_move(pos): pass
Run the program. Verify it has a title screen and you can start the game and see the player. (That is all it will do until we fill in the remaining functions.)
Replace the definition of
player_input() with this:
def player_input(): if keyboard.up: player.vy += 0.1 if keyboard.down: player.vy -= 0.1 if player.vy < 1: player.vy = 1 if keyboard.right: player.vx += 0.4 if keyboard.left: player.vx -= 0.4 player.x += player.vx
Run the program. Verify the player can move left and right and has momentum. Try adjusting the speed or making a limit so you can't go too fast.
Generate the walls
We already have code to draw the walls, but currently
lines is empty
so nothing gets drawn. Replace the function
this. Note that we immediately call
generate_lines() after defining it
to generate the walls for the start of the game.
def generate_lines(): global wall_gradient, left_wall_x gap_width = 300 + math.sin(distance / 3000) * 100 while len(lines) < HEIGHT: pretty_colour = (255, 0, 0) lines.insert(0, (left_wall_x, gap_width, pretty_colour)) left_wall_x += wall_gradient if left_wall_x < 0: left_wall_x = 0 wall_gradient = random.random() * 2 + 0.1 elif left_wall_x + gap_width > WIDTH: left_wall_x = WIDTH - gap_width wall_gradient = -random.random() * 2 - 0.1 generate_lines()
Run the program. Change the colour of the walls from red to green.
Make the walls colourful
Modify the line that sets the colour of the generated line to this:
pretty_colour = (255, min(left_wall_x, 255), min(time * 20, 255))
scroll_walls() function so it removes lines from the bottom
of the screen according to the player’s vertical velocity.
def scroll_walls(): global distance for i in range(0, int(player.vy)): lines.pop() distance += 1
Change the amount of the forward acceleration to make the game faster or slower.
Currently the player can move through the walls - we don’t want to allow this. Also we want the player to lose all their velocity each time they collide as a penalty.
def wall_collisions(): a, b, c = lines[-1] if player.x < a: player.x += 5 player.vx = player.vx * -0.5 player.vy = 0 if player.x > a + b: player.x -= 5 player.vx = player.vx * -0.5 player.vy = 0
Make the collision more bouncy, i.e. the player bounces further when he hits the wall.
Currently the player has infinite time. We want decrease the
variable by how much time has passed and end the game when time runs
def timer(delta): global time, playing, best_distance time -= delta if time < 0: playing = False if distance > best_distance: best_distance = distance
Make the game last for 30 seconds.
The game is easier but perhaps more fun if you can play it with mouse. Pygame will call this function for us automatically.
def on_mouse_move(pos): x, y = pos player.x = x player.vy = (HEIGHT - y) / 20
Ideas for extension
- Draw a new image for the player. Make the Actor show a different image depending on if the player is steering left or right.
- Give the player a goal distance that must be reached. If the player reaches this distance he gets extra time added to allow him to continue.
- Add sound effects and music.
- If you have a larger screen, make the game window taller (and make sure the alien appears at the bottom still).