Break Into Program – TransAm

This is another of our occasional blog posts on programming retro games using techniques and languages that are familiar to kids. Retro games are often a good starting point, as they are usually fairly simple, yet with entertaining game play.

TransAm is a classic Sinclair Spectrum Game by Ultimate Play the Game, and takes the form of a road race across America. It’s a top-view game, and is one of my personal favourites.

Retro fact of the day: This game was originally developed to work on the 16K ZX Spectrum, so the code and graphics had to be less than 10K in size, as the screen RAM took up over 6K.

A video of the original game can be found here:

We looked at programming a version of this game in Scratch, and there is also a version for Python written using the PyGame library.

Link to Scratch here

The Python code is listed below, and can be cut and pasted into Python. If you are new to Python, we’ve written guides for installing Python and PyGame.

The graphics for this game can be downloaded here:

Graphics – TransAm

Unzip into the graphics directory (we put them in C:\Python\Dev\CoderDojo\Graphics).

Enjoy!

The Science Bit (Python Code)

[python]
# Import the libraries
#
import pygame
import random

# Initialise PyGame
#
pygame.init()

# Some global variables
#
win_w = 800 # Window width
win_h = 600 # Window height
ticker = 0 # Ticker, increments every pass of the game loop

# Lookup table of scroll speeds (one for each rotation of the car)
#
scroll_x = [ 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2, -2, -2, -2, -2, -1 ]
scroll_y = [ -2, -2, -2, -1, 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2, -2 ]

scroll_dir = 0; # Scroll direction
scroll_speed = 1; # Scroll speed multiplier

sprites = [] # List of sprites

# Create a screen surface to draw on, and a clock
#
screen = pygame.display.set_mode((win_w, win_h))
clock = pygame.time.Clock()

# Set the key repeat to 1ms delay, 1ms repeat
#
pygame.key.set_repeat(1,1)

# Load some images ————————————————————-

# Load the car rotation graphics into an array
#
image_car = [None] * 16
for i in range(16):
image_car[i] = pygame.image.load("../Graphics/transam_%s.png" %(i)).convert()
image_car[i].set_colorkey((0,0,0))

# Load the ground dot
#
image_dot = pygame.image.load("../Graphics/dot_4x4_yellow.png").convert()

# The Sprite class ————————————————————-

class Sprite:
#
# Some basic variables, just for this sprite
#
active = None # Whether the sprite is active or off
xp = None # X position of the sprite
yp = None # Y position of the sprite
image = None # Sprite image
type = None # Sprite type (string)
fn_move = None # Function to move the sprite

#
# This is called when we initialise the Sprite class
#
def __init__(self):
self.active = False

#
# A class function to move the sprite
#
def move(self):
if(self.active and self.fn_move is not None):
self.fn_move(self)
return

#
# A class function to draw the sprite
#
def draw(self):
if(self.active):
screen.blit(self.image, (self.xp, self.yp))

# Set up a list of Sprites
#
max_sprites = 200 # Number of sprites we want to create
sprites = [Sprite() for i in range(max_sprites)] # Create a list of Sprites

# Helper functions ————————————————————

# Find index of next available free sprite slot in list
# Returns index in array of sprites, or -1 if no slots left
#
def find_next_available_sprite():
for i in range(0, max_sprites):
if sprites[i].active == False:
return i
return -1

# Movement functions ———————————————————-

# Move the player
#
def move_player(self):
global scroll_dir, scroll_speed

# Only read the keyboard every 4th frame
#
if(ticker % 4 == 0):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and scroll_speed >= 1:
self.rotate = (self.rotate – 1) % 16
if keys[pygame.K_RIGHT] and scroll_speed >= 1:
self.rotate = (self.rotate + 1) % 16
if keys[pygame.K_UP] and scroll_speed < 8: scroll_speed += 1 if keys[pygame.K_DOWN] and scroll_speed > 0:
scroll_speed -= 1
self.image = image_car[self.rotate]
scroll_dir = self.rotate
scroll_speed -= 0.1
if scroll_speed < 0:
scroll_speed = 0

def move_ground(self):
global scroll_x, scroll_y, scroll_dir, scroll_speed
self.xp = (self.xp – scroll_x[scroll_dir] * scroll_speed) % win_w
self.yp = (self.yp – scroll_y[scroll_dir] * scroll_speed) % win_h

# Stubbed functions ————————————————————

# Called when the game initialises
#
def Initialise_Game():
# Create the player sprite
#
sprites[max_sprites – 1].sprite_type = "player" # This is the sprite type
sprites[max_sprites – 1].xp = win_w//2 # Set X position to centre of screen
sprites[max_sprites – 1].yp = win_h//2 # Set Y position to centre of screen
sprites[max_sprites – 1].fn_move = move_player # Set function to move sprite
sprites[max_sprites – 1].active = True # Set the sprite to be active
sprites[max_sprites – 1].rotate = 0;

# Create some ground dot sprites
#
for i in range(0, 50):
sprites[i].sprite_type="bg"
sprites[i].image = image_dot
sprites[i].xp = random.randint(0, win_w)
sprites[i].yp = random.randint(0, win_h)
sprites[i].fn_move = move_ground
sprites[i].active = True

return

# Called when the level initialises
#
def Initialise_Level():
return

# Called when the level ends
#
def End_Level():
return

# Called when the game ends
#
def End_Game():
return

# Main Game Loop —————————————————————

Initialise_Game()

# This outer loop keeps going until end_game is True
#
end_game = False
while not end_game:

Initialise_Level()

# This outer loop keeps going until end_level is True
#
end_level = False
while not end_level:

#
# Pygame event handler
#
for event in pygame.event.get():
# Check for window close
if event.type == pygame.QUIT:
end_level = True
end_game = True

# Move everything by calling the Sprite’s move function
#
for i in range(0, max_sprites):
sprites[i].move()

# Clear the screen
#
screen.fill((0,0,0))

# Draw the Sprites by calling their draw function
#
for i in range(0, max_sprites):
sprites[i].draw()

# Update the surface to the screen and lock at 60fps
#
pygame.display.flip()
clock.tick(60)
ticker += 1

# Level over
#
End_Level()

# Game over!
#
End_Game()
pygame.quit()
[/python]