A7: An Avoider Game
Avoider Games
An avoider game is a class of games where you must navigate your mouse pointer past obstacles. A basic avoider game is essentially a maze, while more complicated ones might have moving enemies, traps, helpful tools, and so on. A simple example of this genre of game is at
https://armorgames.com/play/519/mouse-avoider-2 (Links to an external site.)
Note that you will maybe have to enable Flash in your browser to be able to play it.
The goal for this assignment is to make an avoider game.
Getting Started
Download the AvoiderStarter PyCharm project: A7.zip. You may not use or modify the sample map or sample characters in your own project – this is just to give an idea of how it might work. Make your own.
Look carefully at the AvoiderGame code. It loads a map to use as a background, then reports on the color underneath the mouse cursor and shows how to make animation cycles of different lengths using an overall frame counter. These are key concepts you will need to make your own game, so work to understand them.
There are two functions in the starter code. One is a sprite loader that reads a folder of images and stores them in a list. This function can be used to read Piskel (https://www.piskelapp.com/ (Links to an external site.)) sprites exported as a zip file and then extracted to a folder.
The other function moves a rectangle between two points as a frame count increases – this can be used to move a guard sprite, for example. You do not need to use this, but you may find it useful.
Installing Pygame
Follow the directions and example from the lectures next week to install pygame in your PyCharm project.
Making Your Game
The basic requirements for the game are simple (worth 100 points out of 200 total):
Make a background image for the avoider game. There should be a safe color, a start color, and an end color.
The basic mechanics are the player clicks on the start color to begin (look at MOUSEBUTTONDOWN events in pygame), then moves the mouse to the end color. If anything besides the start, end, or safe color is touched, the game is over. Use an if-statement to check this.
If the player wins by bringing the mouse to the end color, bring up a win image or sprite. If the player loses, bring up a lose image or sprite. Wait a sufficient amount of time and then close the program (or go to the next level).
You must demonstrate these basic requirements during the lab on Apr 17th to receive points for that lab. We will post more information about this requirement next week.
Additional requirements are to make the game more interesting (worth 100 points out of 200 total):
There should be at least 2 levels. When the end color is reached of level 1, a new background should be loaded, and the player needs to click on the start color again to begin that level.
If the player loses during a level, you should ask the player if they want to try again or quit.
Each level needs at least 2 animated sprites that move in different ways. The sprite can make the player lose, or the sprite can somehow help the player. Make the sprite in Piskel or some other program, but the files need to be in a folder and named like Piskel does to be loaded by the starter code.
A timer is needed to show how fast the player completes each level. Display the time on the screen, building on the example from the starter code.
You need to be creative and add something to make the game interesting. A perfectly done game with two levels with two sprites each that bounce in different ways will be considered a B grade game. Your creative elements are needed to move into the B+ and A range. Think about animating the whole map (or animating a sprite that looks like part of the map on top of the map), using image rotation, adding different kinds of sprites and triggers and traps. Keep track of top scorers by reading and writing to a score file. Have fun with it!
Write a short description of the creative elements of the game so that we are sure to notice them. A paragraph or short bullet list is fine for this. Add a pdf or txt file in the folder with the name “CreativeParts” .txt or .pdf. Make sure it gets zipped up with the rest of the materials.
import sys, pygame, math
# Finished game author:
#
#
# This function loads a series of sprite images stored in a folder with a
# consistent naming pattern: sprite_##. It returns a list of the images.
def load_piskell_sprite(sprite_folder_name, number_of_frames):
frames = []
# Figure out how many digits are in the frame number
padding = math.ceil(math.log(number_of_frames,10))
for frame in range(number_of_frames):
folder_and_file_name = sprite_folder_name + “/sprite_” + str(frame).rjust(padding,’0′) +”.png”
frames.append(pygame.image.load(folder_and_file_name).convert_alpha())
return frames
# This function bounces a rectangle between the start and end pos. The bounce happens over num_frame frames.
# So the bigger the num_frame value is, the slower it goes (the bounce takes more frames).
# The rect is modified to be at the new position – a rect is not returned.
# Start and end pos are tuples of (x,y) coordinates on the window. You will likely need to experiment to find
# good coordinates.
def bounce_rect_between_two_positions( rect, start_pos, end_pos, num_frame, frame_count ):
if frame_count%num_frame < num_frame/2:
new_pos_x = start_pos[0] + (end_pos[0] – start_pos[0]) * (frame_count%(num_frame/2))/(num_frame/2)
new_pos_y = start_pos[1] + (end_pos[1] – start_pos[1]) * (frame_count%(num_frame/2))/(num_frame/2)
else:
new_pos_x = end_pos[0] + (start_pos[0] – end_pos[0]) * (frame_count%(num_frame/2))/(num_frame/2)
new_pos_y = end_pos[1] + (start_pos[1] – end_pos[1]) * (frame_count%(num_frame/2))/(num_frame/2)
rect.center = (new_pos_x, new_pos_y)
def main():
# Initialize pygame
pygame.init()
map = pygame.image.load(“AvoiderMap.png”)
# Store window width and height in different forms for easy access
map_size = map.get_size()
map_rect = map.get_rect()
# set the background color to something more pleasing than black
background_color = 0, 0, 0
# create the window
screen = pygame.display.set_mode(map_size)
map = map.convert()
# Load the sprite frames from the folder
bad_guy = load_piskell_sprite(“badguy”, 21)
bad_guy_rect = bad_guy[0].get_rect()
star = load_piskell_sprite(“star”,2)
star_rect = star[0].get_rect()
# The frame tells which sprite frame to draw
frame_count = 0;
# The clock helps us manage the frames per second of the animation
clock = pygame.time.Clock()
# Get a font
myfont = pygame.font.SysFont(‘monospace’, 24)
# The started variable records if the start color has been clicked and the level started
started = False
# The is_alive variable records if anything bad has happened (off the path, touch guard, etc.)
is_alive = True
# Hide the arrow cursor and replace it with a sprite. Don’t make this big or the checking for collisions
# gets more complicated than just looking at the color under the cursor tip.
pygame.mouse.set_visible(False)
# This is the main game loop. In it we must:
# – check for events
# – update the scene
# – draw the scene
while is_alive:
# Draw the background
screen.blit(map, map_rect)
# Check events by looping over the list of events
for event in pygame.event.get():
if event.type == pygame.QUIT:
is_alive = False
# Add a mouse down event to look for clicking on the start color
# Draw a guard
bounce_rect_between_two_positions(bad_guy_rect, (500,300), (520, 150), 200, frame_count)
screen.blit(bad_guy[frame_count%len(bad_guy)], bad_guy_rect)
# Now check for the color under the cursor. Don’t draw the cursor sprite yet or the
# color will be the cursor sprite color, not the scene.
pos = pygame.mouse.get_pos()
color_at_cursor = screen.get_at(pos)
# In the starter code I just print the color. Don’t print it in your game as it will slow
# things down. But you can use this to see the colors you need to be looking for to see if the cursor
# is in a safe spot.
# Note that the color has 4 values – the 4th is alpha, or transparency. If you want to compare colors
# make sure that you compare all the values. An example would be
# color_at_cursor == (255, 0, 0, 255)
# to see if the cursor is over a pure red area.
print(color_at_cursor)
# Once you have the color at the cursor, check to see if you have gone off the path or if you have
# touched the end color. You need to write code for this. The logic is that you need to see if the
# level has been started, if you are at the end color, and if it is touching something bad.
# Now move and draw the cursor sprite. Divide the frame_count by 10 to slow the cycle down.
star_rect.center = pygame.mouse.get_pos()
screen.blit(star[(frame_count//10)%len(star)], star_rect)
# Write some text to the screen. You can do something like this to show the time going by.
label = myfont.render(“Level 1!”, True, (255,255,0))
screen.blit(label, (20,20))
# Every time through the loop, increase the frame count.
frame_count += 1
# Bring drawn changes to the front
pygame.display.update()
# This tries to force the loop to run at 30 fps
clock.tick(30)
pygame.quit()
sys.exit()
# Start the program
main()
Back ground needs to be unique, and same with sprites, and needs to be at least 3