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.
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:
Unzip into the graphics directory (we put them in C:\Python\Dev\CoderDojo\Graphics).
Enjoy!
The Science Bit (Python Code)
# 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()