Making a Game of IT - Day 4
Functions
We’ve been using Python’s built-in functions like str()
, input()
, len()
, and print()
.
Python’s def
keyword — followed by the intended name of your function, its arguments inside parentheses, and a colon —lets us create our own.
The indented code block under the def
line constitutes the body of the function… it’s executed every time the function is called.
After we def
the function we call it by its name followed by a set of parentheses with the arguments it needs inside.
In this first example, the function hello_world()
takes no arguments.
def hello_world():
print("Hello world!")
hello_world()
In this next example, we see how functions can access variables from outside their code block.
name = "Rear Admiral Grace Brewster Murray Hopper"
def hello_person():
print("Hello", name)
hello_person()
name = "Dr. Alan Mathison Turing"
hello_person()
However, functions can’t change variables outside their code block
name = "Rear Admiral Grace Brewster Murray Hopper"
def name_change():
name = "inside name"
name_change()
print(name)
Variables inside functions override variables from outside functions.
name = "Scarlin Hernandez"
def hello_person():
name = "inside name"
print("Hello", name) # This should print 'inside name'
hello_person()
print("Outside", name) # This should print 'Scarlin Hernandez'
This function takes one argument.
def hello_person(name):
print("Hello", name)
hello_person("Dr. Mark E. Dean")
hello_person("Dr. Alan Mathison Turing")
This function takes two arguments.
def repeat_hello(n, name):
for i in range(n):
print("Hello, name, i)
repeat_hello(1, "Katherine Johnson")
repeat_hello(7, "Roy L. Clay, Sr.")
Functions can use the return
keyword to halt and give a value back to the caller.
def oh_hai(name):
return "oh, hai " + name
print("do more stuff") # This isn't executed because it comes after the return
result = oh_hai("Dr. Fei-Fei Li")
print(result)
Why are functions useful?
- re-use of code inside the function (you have to type less code)
- compartmentalize code so that high-level structure of your program is easier to follow
- experiment with/test individual components of your program
Jumping around (platforming)
The example game shown below is a simple platformer where players control sparty with the goal of reaching the giant coin in the top right corner of the screen.
To run this game you will need the coin.png and sparty.png files!
"""
This activity is intended to introduce you to Python and a
library for creating video games, PyGame. Thoughout the activity,
you'll see comments (which have a # in them) giving you instructions.
Important note: Python is very sensitive to spaces and tabs, please
ensure that as you change lines as instructed, that you do not change the
indentation of the code.
By: Josh Nahum
"""
# This section of code is needed to get everything set up.
import pygame
import sys
pygame.init()
clock = pygame.time.Clock()
# This section of code determines the size of the game window
# and sets the title of the window (orginally 800, 600, and Sparty Platformer).
# Try adjusting the game window size and the title.
WINDOW_SIZE = [1200, 600]
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Sparty Platformer")
# These are some names for common colors.
# A color is determined by 3 numbers (how much red, green, and blue a color has).
# Each of these three numbers use be between 0 and 255.
# Try making a custom color (we'll use it later to be the background of our game.)
BLACK = [0, 0, 0]
WHITE = [255, 255, 255]
GREEN = [0, 255, 0]
RED = [255, 0, 0]
# This section of code loads a PNG image of Sparty.
# The name of the file is Sparty.png
# If you download another small image off the web,
# place it in the same folder as this activity.py file,
# and replace the filename with the one you downloaded,
# then you will have a different sprite to play width.
# I've included a file called "helmet.png" as a alternative.
sparty_img = pygame.image.load("Sparty.png")
sparty_rectangle = sparty_img.get_rect()
# This section of code is similar to the one above,
# except it loads an image of the goal Sparty is trying
# to get to, a gold coin.
goal_img = pygame.image.load("coin.png")
goal_rectangle = goal_img.get_rect()
# Try changing Sparty's start location by changing the zeroes
# in the next to lines of code to other positive number.
# Notice that larger y values are lower on the screen.
sparty_location_x = 0
sparty_location_y = 0
# These lines determine the height and color of the platforms,
# feel free to adjust these.
platform_height = 10
platform_color = GREEN
# This code makes a single platform that runs along the bottom of the screen,
# please don't change this code.
screen_rectangle = screen.get_rect()
bottom_platform = pygame.Rect(
screen_rectangle.left,
screen_rectangle.bottom - platform_height,
screen_rectangle.right - screen_rectangle.left,
platform_height)
# This is a list of platforms, initially just 3.
# Each platform is made with 4 numbers (all denoted in pixels)
# 1. The x coordinate of the left-most edge of the rectangle.
# 2. The y coordinate of the top-most edge of the rectangle.
# 3. The width of the rectangle.
# 4. The height of the rectangle (I like my platforms to be the same height of platform_height).
# But you can use different heights if you prefer.
# Add enough platforms to enable Sparty to reach the goal.
platforms = [
bottom_platform,
pygame.Rect(100, 400, 300, platform_height),
pygame.Rect(300, 500, 150, platform_height),
pygame.Rect(400, 300, 150, platform_height),
pygame.Rect(500, 200, 150, platform_height),
pygame.Rect(600, 100, 500, platform_height),
]
# These next 2 lines determine the intial x and y velocities of the
# player character. The values are the number of pixels to shift the character
# for each frame of animation drawn.
sparty_velocity_x = 0 # Larger means moving faster rightwards
sparty_velocity_y = 0 # Larger means moving faster downwards
# Sparty is subject to gravity (orginally set to 0.3 for Earth),
# try making this value smaller to simulate
# jumping on the moon (0.05). Warning, setting this value too high
# will break the physics of the game.
GRAVITATIONAL_CONSTANT = 0.1
# This variable is used later to determine if Sparty is standing
# on a platform (to determine if Sparty is allowed to jump.)
is_standing = False
# This code is the game loop that listens to key presses,
# calculates the physics,
# draws the background, platforms and Sparty.
while True:
# The following section of code is responsible for the event loop,
# which listens for keyboard events and does things (like
# changing the player's velocity in response.)
for event in pygame.event.get():
# If the user clicks the 'X' to close the window, this closes the game and program
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
# Should Sparty be allowed to jump when not standing?
# If so uncomment the next line of code and comment out the line after it
# This will remove the requirement that Sparty be standing to initiate a jump
if event.key == pygame.K_UP and is_standing:
# if event.key == pygame.K_UP:
# Notice the next 3 numbers (orginally -5, 4, and -4)
# This values are what the x and y velocities are changed to on button presses
# Try changing them to make Sparty jump or run faster.
sparty_velocity_y = -5
if event.key == pygame.K_RIGHT:
sparty_velocity_x = 4
if event.key == pygame.K_LEFT:
sparty_velocity_x = -4
# This code ensures that when you release the left or right arrow keys
# Sparty stops moving.
# Try commenting out the next 5 lines of code to see what happens
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
sparty_velocity_x = 0
if event.key == pygame.K_LEFT:
sparty_velocity_x = 0
# Physics
# With every frame of animation drawn, we need to do some math to calculate
# where Sparty should be drawn.
# The next line makes Sparty's downward velocity increase due to gravity
sparty_velocity_y = sparty_velocity_y + GRAVITATIONAL_CONSTANT
# The next 2 lines calculate Sparty's coordinates given their x and y velocity.
sparty_location_y = sparty_location_y + sparty_velocity_y
sparty_location_x = sparty_location_x + sparty_velocity_x
# The next four sections of code do all the drawing of objects to the screen.
# Section 1
# Set the screen background
# Fill free to change this color to the name of your custom color.
screen.fill(WHITE)
# Section 2
# Draw all the platforms
for platform in platforms:
pygame.draw.rect(screen, platform_color, platform)
# Section 3
# Draw Sparty (this is the hardest because we need to use Sparty's
# x and y location to determine where to place them).
sparty_rectangle.left = sparty_location_x
sparty_rectangle.top = sparty_location_y
# If you would prefer Sparty be replaced with a RED rectangle, comment
# out the next line of code and uncomment out the line after.
screen.blit(sparty_img, sparty_rectangle)
# pygame.draw.rect(screen, RED, sparty_rectangle)
# Section 4
# Draw the goal (a gold coin) in the top right of the screen.
goal_rectangle.top = screen_rectangle.top
goal_rectangle.right = screen_rectangle.right
screen.blit(goal_img, goal_rectangle)
# The order in which things are drawn affects what objects
# occlude (cover up) others. Try swaping the order of Section 2
# with Section 3 and see what happens if you try to jump up through a platform.
# The following section of code determines if Sparty is standing
# on a platform and ensures they don't pass through a platform.
# This code is a bit complicated because it needs to check which platform Sparty
# is touching and ensures that Sparty
# can only land on a platform if they are descending.
is_standing = False
index = sparty_rectangle.collidelist(platforms)
if index != -1:
touching_platform = platforms[index]
is_just_touching_down = sparty_rectangle.bottom - touching_platform.top < platform_height
# To see what happens if Sparty can land on a platform without descending,
# Comment out the following line and uncomment out the line following.
# Be sure to try jumping upwards through a platform.
is_descending = sparty_velocity_y > 0
# is_descending = True
if is_just_touching_down and is_descending:
sparty_location_y = touching_platform.top - sparty_rectangle.height
is_standing = True
sparty_velocity_y = 1
# This section of code determines if Sparty is touching the goal
# (the image of the gold coin). If it is, move Sparty to position (0, 0)
# So that the game can repeat.
if sparty_rectangle.colliderect(goal_rectangle):
sparty_location_x = 0
sparty_location_y = 0
# These two lines of code are responsible for displaying all objects on the screen
# and fixing the frame rate of our game to 30 frames per second.
pygame.display.update()
# Try adjusting the FPS to 10
clock.tick(30)
# Congratulations you made it to the end!
# This activity was originally created by Dr. Josh Nahum
# and he can be reached at nahumjos@cse.msu.edu
# For more information about PyGame (and Python), check out
# this free book by Al Sweigart titled, "Invent Your Own Computer Games With Python"
# http://inventwithpython.com/
|>> download sparty-platformer/platformer.py
Angle shooting
import sys
import math
import pygame
pygame.init()
SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 800
FPS = 60
LIGHT_GREY = [230, 230, 230]
RED = [150, 0, 0]
BLACK = [0, 0, 0]
BLUE = [0, 0, 150]
PROJECTILE_SPEED = 5
PROJECTILE_RADIUS = 10
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
pygame.display.set_caption("Pointer game")
clock = pygame.time.Clock()
mid_point = [int(SCREEN_WIDTH / 2), int(SCREEN_HEIGHT / 2)] # We'll be shooting projectiles from the middle of the screen
projectiles = [] # Here's where we'll track projectiles
while True:
screen.fill(LIGHT_GREY)
mouse_pos = pygame.mouse.get_pos() # We'll be using the mouse position to fire projectiles from
pygame.draw.circle(screen, RED, mid_point, 20) # Draw a circle in the middle of the screen (where we'll shoot from)
pygame.draw.circle(screen, RED, mouse_pos, 10) # Draw a circle to tell the player where they're targetting
# Draw a line from the mid point to the mouse position
pygame.draw.line(screen, BLACK, mid_point, mouse_pos, 3)
############################################################################
# Draw all of the projectiles, track on screen vs off screen
############################################################################
# Let's get the unit vector for the line we just drew
# - at the same time, we'll track what projectiles are still on the screen
on_screen_projectiles = []
for projectile in projectiles:
# Move the projectile in the correct direction
# - projection["direction"] is a unit vector pointing in the direction we want this projectile to travel
# - We multiply the direction vector's x and y by our speed to have the projectile
# move in the correction direction at the correct speed
projectile["current_loc"][0] += (PROJECTILE_SPEED * projectile["direction"][0])
projectile["current_loc"][1] += (PROJECTILE_SPEED * projectile["direction"][1])
# For convenience, we'll grab the current location (that we just calculated)
# for this projectile
cur_loc = projectile["current_loc"]
pygame.draw.circle(screen, BLUE, [int(cur_loc[0]), int(cur_loc[1])], PROJECTILE_RADIUS)
# Detect whether the projectile is off of the screen
# - To do that, we can make a bounding box around the circle and
# detect whether or not that circle's bounding box is colliding with
# a bounding box around the entire screen
projectile_box = pygame.Rect([0,0],[0,0])
projectile_box.centerx = cur_loc[0]
projectile_box.centery = cur_loc[1]
projectile_box.width = 2*PROJECTILE_RADIUS
projectile_box.height = 2*PROJECTILE_RADIUS
# Use that bounding box to detect if something goes off screen
if screen.get_rect().colliderect(projectile_box):
on_screen_projectiles.append(projectile)
projectiles = on_screen_projectiles
############################################################################
# Here's our event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
# Calculate the vector from where we're shooting (mid_point) to
# where we're aiming (mouse_pos)
# - If instead, you were shooting from a character's location, you could use
# that character's location instead of the mid_point of the screen.
line_vec = [mouse_pos[0] - mid_point[0], mouse_pos[1] - mid_point[1]]
# How long is the vector? (lookup python's math.hypot function in the
# python docs)
vec_len = math.hypot(line_vec[0], line_vec[1])
# Calculate the unit vector => we'll use this to specify the direction of the projectile
unit_vec = [line_vec[0] / vec_len, line_vec[1] / vec_len]
# Add new projectile to projectiles list
projectiles.append({"current_loc": [mid_point[0], mid_point[1]], "direction": unit_vec})
pygame.display.update()
clock.tick(FPS)
|>> download pointer.py
Interacting with an NPC
'''
Chatty NPC example
'''
import sys
import pygame
pygame.init()
SCREEN_WIDTH = 500
SCREEN_HEIGHT = 500
FPS = 60
RED = [150, 0, 0]
LIGHT_GREY = [230, 230, 230]
BLACK = [0, 0, 0]
BLUE = [0, 0, 150]
ORANGE = [245, 180, 65]
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
pygame.display.set_caption("Chatty NPC")
clock = pygame.time.Clock()
font = pygame.font.SysFont('impact', 18)
NPC_WIDTH = 25
NPC_HEIGHT = 25
NPC_X = int(0.75 * SCREEN_WIDTH)
NPC_Y = int(0.5 * SCREEN_HEIGHT)
NPC_INTERACT_RANGE = 10 # How many pixels away from the NPC can you interact with it?
best_bud = {"rect": pygame.Rect([NPC_X, NPC_Y], [NPC_WIDTH, NPC_HEIGHT]),
"interaction_rect": pygame.Rect([NPC_X - NPC_INTERACT_RANGE, NPC_Y - NPC_INTERACT_RANGE], [NPC_WIDTH + (2*NPC_INTERACT_RANGE), NPC_HEIGHT + (2*NPC_INTERACT_RANGE)]),
"can_interact": False,
"talkietalkie": "Hi there my good friend!",
"text_visible": False}
CHAR_START_X = 0
CHAR_START_Y = 0
CHAR_WIDTH = 15
CHAR_HEIGHT = 15
CHAR_SPEED = 5
character = pygame.Rect([CHAR_START_X, CHAR_START_Y], [CHAR_WIDTH, CHAR_HEIGHT])
while True:
screen.fill(LIGHT_GREY)
# Update character
pressed_keys = pygame.key.get_pressed()
cur_char_vel = [0, 0]
# - vertical movement
if pressed_keys[pygame.K_w]:
cur_char_vel[1] = -1*CHAR_SPEED
elif pressed_keys[pygame.K_s]:
cur_char_vel[1] = CHAR_SPEED
# - horizontal movement
if pressed_keys[pygame.K_d]:
cur_char_vel[0] = CHAR_SPEED
elif pressed_keys[pygame.K_a]:
cur_char_vel[0] = -1*CHAR_SPEED
character.left += cur_char_vel[0]
character.top += cur_char_vel[1]
# Is the character in range to interact with the NPC?
if character.colliderect(best_bud["interaction_rect"]):
best_bud["can_interact"] = True
else:
best_bud["can_interact"] = False
best_bud["text_visible"] = False
############################################################################
# DRAWING
############################################################################
# Draw our best bud
# pygame.draw.rect(screen, BLACK, best_bud["interaction_rect"])
pygame.draw.rect(screen, RED, best_bud["rect"])
pygame.draw.rect(screen, BLUE, character)
if best_bud["text_visible"]:
# Text should be visible!
text_surf = font.render(best_bud["talkietalkie"], True, BLACK)
text_box = text_surf.get_rect()
text_box.centerx = best_bud["rect"].centerx
text_box.y = best_bud["rect"].y - text_box.height - 5
pygame.draw.rect(screen, ORANGE, text_box)
pygame.draw.rect(screen, ORANGE, text_box, 3)
screen.blit(text_surf, text_box)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_e:
if best_bud["can_interact"]:
best_bud["text_visible"] = not best_bud["text_visible"]
pygame.display.update()
clock.tick(FPS)
|>> download chatterbot.py