Python Cheat Sheet - Week 5
Here's something approaching a real game. And again, you're invited to use your creativity, which is a bit much really, when you're trying to follow a tutorial. Come on, let me just cut and paste! But you do have to make decisions, and those decisions will affect things further down the line, meaning this project takes a bit more navigation.
Code is shown in blue.
Jump to the code for my example - Bagel-Hating Shark.
Setting up
To get started, you need to make some key decisions. The first tricky one is what to make your character out of - an image, emoji, text or custom drawn graphics. You'll probably find image easiest, so choose that for now if you're worried about time. And then you have to decide how to draw the obstacles too...
They see me scrollin...
Making the obstacles scroll is a neat trick. Rather than adding one onto the position every time, we add the frame count, which means we don't need to keep track of the count. Then we use a special operator called 'modulus'. This basically gives the remainder of a division. So every time the number gets to a multiple of the screen height, it goes back to zero. Neat, huh?
Objects everywhere
There's another trick here, and it relies on the fact that rand() isn't entirely random. It looks random, but it's actually a crazy mathematical function: hard is it is to predict, if you feed the same number into it each time you'll get the same numbers out. seed() determines what number it starts at, and so by seeding the same number at the start of the game loop, we always get the same series of 'random' numbers out, and our obstacles are always drawn in the same place (except for the scrolling aspect, of course). We then use a FOR loop to save us repeating the drawing code.
There's an obvious problem with this, as you'll see when you play these games - the obstacles are always drawn in the same place. This makes them predictable, and sometimes there's even a gap where none ever appear, and you can just cruise on till forever. How would you change it so that each game is different? And more challenging than that, how would you change it so that new obstacles appear in different places in the same game?
Collision course
Whole books have been written on collision detection, but here there's an easy way to do it:because your character is only moving in one direction, they will always collide from the front, so we can approximate the front-centre as a collision point. That means if we check the colour at this point, and it's not the background (safe) colour, we know that we've crashed.
You can see why this is a limited technique: it relies on there either being only one type of object in the game (you couldn't collect) a power-up, for example), or on knowing the colour of everything. And what if something could hit you from behind?
What else is new?
Well, not much. This is all about practising what you've learned, and seeing how much can be done with a relatively small number of commands. It's also about structuring the code, getting your lines in the right order, and seeing how game makers use simple tricks to create impressive functionality
There's so much you can do with the template you've created here: you could change the direction, add power-ups to slow down or speed up the action, give the player a number of lives... have a play around, and see what you can come up with.
The Code
Here's the code Bagel-Hating Shark. Feel free to copy it into your editor and play around with it.
#!/bin/python3
from p5 import *
from random import randint, seed
# Include global variables here
level = 1
obstacle_count = 6
score = 0
def setup():
# Put code to run once here
size(400, 400)
global player
player = load_image('shark.png') # Load your image
text_size(32) # Controls the size of the emoji
text_align(CENTER, TOP) # Position around the centre
def draw_player():
global safe, score, level
player_y = int(height * 0.8) # Positioned towards the screen bottom
collide = get(mouse_x, player_y).hex
if collide == safe.hex:
image(player, mouse_x-28, player_y, 60, 60)
score += level
else:
tint(255,0,0)
image(player, mouse_x-28, player_y, 60, 60)
no_tint()
level = 0
def draw_obstacles():
global level, obstacle_count # Use the global level
if frame_count % height == height - 1 and level < 5:
level += 1
obstacle_count +=3
print('You have reached level', level)
seed(12345679) # Any number is fine
for i in range(obstacle_count):
ob_x = randint(0, height)
ob_y = randint(0, height) + frame_count
ob_y %= height # Wrap around
text('🥨', ob_x, ob_y) # Replace with your obstacle
def draw():
# Put code to run every frame here
global safe
safe = Color(0, 100, 100) # Add the colour of your theme
if level > 0:
background(safe)
draw_obstacles()
draw_player()
message = 'Score: ' + str(score)
text_size(18)
fill(255)
text(message, width/2, 10)
text_size(32)
else:
text_size(40)
fill(255)
text('Game Over', width/2, height/2)
text_size(32)
# Keep this to run your code
run()