Designing Objects¶
Now that we are starting to write our own objects, we will start designing our own objects!
By the end we will have the following file structure:
We will start with the game loop. Create a new file called “game.py” and start putting these into there:
The top¶
The top of the file is pretty standard. I have added extra imports to handle different files.
1 2 | import pygame
from settings import *
|
The settings.py file is:
Game __init__
function¶
Creating a class
for our game loop means we can organize all of the functionality easier!
1 2 3 4 5 6 7 8 9 10 11 | class Game:
def __init__(self):
self.walls = []
self.hero = None
self.done = False
pygame.init()
self.screen = pygame.display.set_mode(WINDOW_SIZE)
self.clock = pygame.time.Clock()
pygame.display.set_caption(TITLE)
|
Checkpoint questions:
- What would instantiating this class look like?
- What kinds of things could you add into the initial function?
Game Loop¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def run(self):
while not self.done:
#### EVENT CHECK SECTION
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
## extra stuff will go here
### clear the screen
self.screen.fill(WHITE)
## extra stuff will go here
#### update the display and move forward 1 frame
pygame.display.flip()
# --- Limit to 60 frames per second
self.clock.tick(FPS)
|
Checkpoint questions:
- Given that you already answered how the class could be instantiated, how would you run this function?
- Can you predict what it will do? Try and run it now.
The Hero¶
Let’s create the hero class. You can use the one you wrote from last week.
Put it by itself into a hero.py file and change the top of “game.py” to the following:
1 2 3 | import pygame
from settings import *
from hero import *
|
Now, you should have a hero from last week! It should go into the hero file. I’m going to show the bare bones here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | class Hero:
def __init__(self, x, y, w, h):
''' The hero constructor function '''
self.rect = Rect(x, y, w, h)
## other things could/should go here
def move_right(self, step_size=SPEEDX):
''' Move the hero to the right '''
pass
def move_left(self, step_size=SPEEDX):
''' Move the hero to the left '''
pass
def move_up(self, step_size=SPEEDY):
''' Move the hero up '''
pass
def move_down(self, step_size=SPEEDY):
''' Move the hero down '''
pass
def drift(self):
''' drift across the screen
Note: the implementation should drift x and drift y separately
After the drift in x, it should check for x collisions
After the drift in y, it should check for y collisions
'''
pass
def drift_x(self):
''' Handle the drift in x '''
pass
def drift_y(self):
''' Handle the drift in y '''
pass
def collides_with(self, other_rect):
''' return true if there is a collision '''
pass
def handle_xcollision(self, other_rect):
''' handle collisions going left and right '''
pass
def handle_ycollision(self, other_rect):
''' handle collisions going up and dowon '''
pass
|
We are going to add two new functions to the Hero
class:
update
and draw
.
I will show the functions under the class header below.
Assumption: When update
is called, the hero will be passed a list of walls.
This is so it can check for collisions.
Assumption: When draw
is called, the hero will be passed the screen
.
1 2 3 4 5 6 7 8 9 10 | class Hero:
### all other things here
def update(self, walls):
''' move and check for collisions '''
pass
def draw(self, screen)
''' draw the hero '''
pass
|
Adding the hero into the game¶
Into the Game
class, we will add a new function which will setup everything.
Then, inside the main loop, we will have it run the hero’s functions!
This will also change how the game is instantiated and run.
Updated code is below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | class Game:
def __init__(self):
self.walls = []
self.hero = None
self.done = False
pygame.init()
self.screen = pygame.display.set_mode(WINDOW_SIZE)
self.clock = pygame.time.Clock()
pygame.display.set_caption(TITLE)
def setup(self):
self.hero = Hero(___) ### fill in the underline
def run(self):
while not self.done:
#### EVENT CHECK SECTION
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
## extra stuff will go here
### clear the screen
self.screen.fill(WHITE)
if self.hero is not None:
self.hero.update(self.walls)
self.hero.draw()
#### update the display and move forward 1 frame
pygame.display.flip()
# --- Limit to 60 frames per second
self.clock.tick(FPS)
### this changes the running to:
game = Game()
game.setup()
game.run()
|
Adding Walls¶
We are going to create a wall class. This will let us manage walls better. We should put this in “walls.py”. I have written some code below to make this easier.
Important notes:
- You have to write the
draw
function - The class is able to parse a series of strings into wall placements (see
parse_level
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | class Walls:
def __init__(self):
''' keep track of the walls
you could maybe pass in a COLOR here'''
self.walls = []
def add_wall(self, x, y, w, h):
''' add a single wall'''
self.walls.append(Rect(x,y,w,h))
def parse_level(self, level):
'''Parse a level string into a set of walls. I've made this for you'''
level_width = len(level[0])
wall_width = WIDTH / level_width
level_height = len(level)
wall_height = HEIGHT / level_height
for row_index in range(level_height):
for col_index in range(level_width):
cell = level[row_index][col_index]
if "cell" == "W":
x = wall_width * col_index
y = wall_height * row_index
self.add_wall(x, y, wall_width, wall_height)
def set_example_level(self):
level = [
"WWWWWWWWWWWWW",
"W W W",
"W W W W",
"W W W W",
"W W W",
"WWWWWWWWWWWWW"
]
self.parse_level(level)
def draw(self, screen):
for wall in self.walls:
### fill in the pygame draw code here.
|
In order to get this into the game, we have to do two things:
- Add an import statement
from walls import *
into the game.py - Add this to the
setup
so that the game will make the walls - Add into the game loop a call which draws the walls.
Adding Keyboard Input¶
To get keyboard input, we need to add some extra stuff into the event loop. Specifically, the event loop should handle more complex checks. Also, optionally, we could have the HERO check for game events.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class Game:
## code was here
def run(self):
while not self.done:
#### EVENT CHECK SECTION
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
else:
self.handle_event(event)
### the rest of the game loop here
def handle_event(self, event):
## do various checks for events here.
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
## code here
elif event.key == pygame.K_RIGHT:
## code here
elif event.key == pygame.K_DOWN:
## code here
elif event.key == pygame.K_UP:
## code here
|
Jumping¶
If you’d like to make your hero jump and land on platforms, there are a couple different things that need to happen.
The hero can not respond to the up/down keys anymore
The hero is always moving down
- Inside the moving down, the hero has two speeds:
gravity
, which is the default speed- this number show moving the hero DOWN (so, a positive number if adding to position)
up_energy
, which gets set to some number when a key like SPACE is pressed- then, whenever the hero moves, the
up_energy
decays, for example:up_energy = up_energy * 0.9
- then, whenever the hero moves, the