Level 5: Challenge 2 - IncrediCoders/Python1 GitHub Wiki
Intelli-Scents added this page on March 14, 2023
Let's begin your second challenge for Level 5!
You are going to write code that will add batteries to the game. Paul collects the batteries to regain his health!
To find the Level 5 Challenge 2 code template, open the Level 5 folder, the Challenges folder, and then the Challenge 2 folder. You should already have the files downloaded onto your computer (see Load the IncrediCoders Files). Open the CreeperChase_Challenge2.py file in Visual Studio Code to build the program by following along with my instructions below!
I broke these instructions down into a few sections:
On Line 2, from init import *
, initializes the program (init). This includes code that sets up the file path for the images and text file, gets the fonts ready for this game, sets the window size, and loads the images. This statement allows you to use all the information in that file, for the rest of your program.
On Line 5, def update(delta_time):
updates each time a key is pressed or a button is clicked (these are called an events). This method will continuously check for these events while the game is running.
On Line 6, for event in pygame.event.get():
starts a for loop that runs for every event in the pygame file.
On Line 7, the statement if event.type == pygame.QUIT:
checks if the player has clicked to close the game window.
If the if
statement from Line 7 is true, the stop()
method (on Line 8) closes the game window.
On Line 9, elif key_down(event, " ") and (MY.grounded or MY.level_num > 3):
checks compound conditions. (A "compound condition" is when there are multiple conditions grouped together nested in a single if
statement.) It checks if the spacebar has been pressed and if the player is on the ground (MY.grounded
), or if the player is currently on level 4 or higher (MY.level_num > 3
).
On Line 10, MY.player.velocity.y = -700
makes Paul fall until the player presses the spacebar for Paul to fly upward.
On Line 11, the statement MY.grounded = False
sets the MY.grounded
variable to the Boolean False
, which tells the program that Paul is no longer on the ground. This will affect the animations and Paul's velocity later on in the code.
On Line 12, we're calling the method jetpack_up_animation()
to animate Paul flying upward.
On Line 14, if key_held_down(pygame.K_LEFT):
checks if the left arrow key has been pressed.
On Line 16, MY.player.velocity.x = max(MY.player.velocity.x - PLAYER_ACCEL, -PLAYER_MAX_SPEED)
sets Paul's velocity when he is moving to the left. We're setting the X velocity to the maximum of Paul's current velocity minus his acceleration, or to Paul's max speed. Basically, this sets a limitation so that Paul doesn't run too fast to the left.
On Line 18, MY.player.sprite = MY.paul_run_left
sets Paul's sprite to a new animation, which shows Paul running to the left.
On Line 20, key presses are checked again. The statement elif key_held_down(pygame.K_RIGHT):
checks to see if it the player is holding down the right arrow button.
On Line 22, the code MY.player.velocity.x = min(MY.player.velocity.x + PLAYER_ACCEL, PLAYER_MAX_SPEED)
sets Paul's velocity when he is moving to the right. This is similar to Line 16, where it limits how fast Paul moves, but this time it's for Paul moving to the right instead of to the left.
On Line 24, MY.player.sprite = MY.paul_run_right
is almost exactly like Line 18, but this time we set Paul's sprite to the animation that shows Paul running to the right.
On Line 25, we have an else:
statement. The else:
block runs if no keys have been pressed.
On Line 26, we check if Paul is currently on the ground with the if MY.grounded:
statement. If Paul's on the ground, the program runs Lines 28-33.
On Line 28, if MY.player.velocity.x > 0:
checks Paul's X-axis speed, to see if he's moving to the right. If so, then Lines 29-30 run, where it slows Paul down and then runs the right-facing idle animation.
On Line 29, the variable _MY.player.velocity.x
_ is Paul's speed when he is moving to the right. It is set to max(0, MY.player.velocity.x - PLAYER_DECEL)
, which slows Paul down so that he stops moving.
On Line 30, Paul's sprite is set to show the right-facing idle animation by making MY.player.sprite = MY.paul_idle_right
.
Line 31 is just like Line 28, but with a <
rather than a >
. We check if Paul is moving to the left. Also, note that this is an elif
statement, which runs if Line 28 isn't true.
One Line 32, we set the velocity of Paul to be idle in a similar way as we did on Line 29, but with some key changes. The variable MY.player.velocity.x
is the same, but this time is set equal to min(0, MY.player.velocity.x + PLAYER_DECEL)
instead. The key differences between this line and 29 is min
rather than max
is us, and PLAYER_DECEL
is added rather than subtracted.
This is to slow Paul down and stop him, when he's facing to the right.
On Line 33, we set Paul's MY.player.sprite
variable to the MY.paul_idle_left
variable, which switches Paul into the idle animation.
On Line 35, this else:
statement means Paul is falling and not yet on the ground.
On Line 36, if MY.player.velocity.x > 0:
checks if Paul is facing the right while he's falling.
On Line 37, which is almost identical to line 29, we have this statement:
MY.player.velocity.x = max(0, MY.player.velocity.x - PLAYER_AIR_DECEL)
The only difference between the two lines is that the PLAYER_AIR_DECEL
variable is used rather than PLAYER_DECEL
variable. That's because we're tracking and controlling the velocity of Paul falling while he's in the air!
On Line 38, elif MY.player.velocity.x < 0:
checks if Paul is facing to the left, like Line 31 does.
Line 39 is like Line 32, except it replaces the PLAYER_DECEL
variable with the PLAYER_AIR_DECEL
variable, which makes this statement:
MY.player.velocity.x = min(0, MY.player.velocity.x + PLAYER_AIR_DECEL)
It controls Paul's velocity while he is falling and facing to the left.
On Line 42, the if not MY.grounded:
statement runs if Paul is not on the ground. This is when he is jumping or flying.
On Line 43, if MY.player.velocity.x > 0:
is used to check if Paul is facing to the right, exactly like it is on Lines 28 and 36.
On Line 44, Paul's jetpack animation displays, where he is facing the right. This animation is set with the code: MY.player.sprite = MY.paul_jetpack_right
On Line 45, elif MY.player.velocity.x < 0:
checks if Paul is facing to the left, like on Lines 31 and 38.
Line 46 displays the jet pack animation where Paul is facing to the left. The animation is set with this statement: MY.player.sprite = MY.paul_jetpack_left
On Line 49, the game's gravity is calculated and set using this statement: MY.player.velocity.y = min(MY.player.velocity.y + GRAVITY_ACCEL, PLAYER_TERMINAL_VEL)
Line 52 is the first statement that starts the block of code that checks for hazard collision. The code for hazard in MY.hazards:
runs a loop that checks through all the hazards in the level to see whether Paul has run into one of them.
On Line 53, if MY.player.collides_with(hazard):
is the statement that checks if Paul has touched a hazard tile while he moves through the level.
If Line 53 is true, then Line 54 reduces Paul's health by 2. This is done by the MY.player_health -= 2
statement.
Line 55 checks to see if Paul's health has reached zero with the if MY.player_health <= 0:
statement.
Line 56 restarts the level with the method restart_level(MY.level_num)
if Paul's health has gone down to 0
.
On Line 57, the else:
block runs if Paul's health is greater than 0
after he collides with a hazard tile.
On Line 58, to show that Paul is hurt after running into a hazard tile, this code displays his pain animation:
MY.player.sprite = MY.paul_pain_right
On Line 59, MY.player.location = MY.player_start_position
moves Paul into his start position.
On Line 60, Paul's velocity is reset with the MY.player.set_velocity(0, 0)
statement. In other words, Paul stops moving!
NEW CODE: On Lines 63-66, you will write a for
loop that checks each battery. The block of code checks if Paul touches a floating battery (Line 64), removes that battery that he hits (Line 65), and then increases Paul's health (Line 66). For the instructions, see the next section, "Write Your Own Code."
On Line 69, MY.player.update(delta_time)
updates the movement and animations of Paul, which uses delta_time
as a parameter to keep all the updates in sync.
On Line 72, we set touching = False
before we check for wall collisions. Later on, if Paul does collide with a wall, this statement will be set to True
.
On Line 73, for wall in MY.walls:
is a loop that checks each wall in the current level.
On Line 74, if MY.player.collides_with(wall):
checks if a player hits a wall.
On Line 75, if MY.player.collision[DOWN]:
checks to see if Paul is moving downward when he collided with the wall. If he has, Line 76-78 will run.
On Line 76, MY.player.snap_to_object_y(wall, DOWN)
shows Paul on top of the ground (which is considered a wall), without going through it.
On Line 77, we set Paul's vertical speed MY.player.velocity.y = 0
to stop him from moving.
On Line 78, the statement MY.grounded = touching = True
sets Paul to be grounded (and not using his jetpack, which he uses later in the game).
On Line 79, if MY.player.collision[LEFT]:
checks if Paul has hit a wall on the left.
On Line 80, MY.player.snap_to_object_x(wall, LEFT)
puts Paul's sprite at the position in front of the left wall, which he hit. This prevents Paul from going past the wall.
On Line 81, MY.player.velocity.x = 0
sets Paul's speed to 0
, to stop him from moving through the wall.
On Line 82, touching = True
lets the game know that Paul has hit a wall.
On Line 83, if MY.player.collision[RIGHT]:
checks if Paul collides with a wall to his right.
Line 84 is just like Line 80, but with a different parameter. It uses RIGHT
instead of LEFT
. MY.player.snap_to_object_x(wall, RIGHT)
places Paul's sprite in front of the left wall, which he hit. This prevents Paul from going past the wall.
On Line 85, MY.player.velocity.x = 0
sets his horizontal movement speed to 0 so that he does not move through the wall that he just collided with.
On Line 86, touching = True
captures the information that Paul has now touched a wall.
On Line 87, if MY.player.collision[UP]:
checks if Paul has hit a tile above him. This might be if Paul jumps and hits his head on the ceiling.
On Line 88, like with the other collisions, if Paul has hit a ceiling tile above him, then the function MY.player.snap_to_object_y(wall, UP)
is used to make sure Paul is positioned right below the ceiling tile and doesn't go through the ceiling.
On Line 89, MY.player.velocity.y = 0
makes sure Paul is no longer moving and doesn't go through the ceiling above him. Lines 88 and 89 work together to keep Paul from going through the ceiling.
On Line 90, like when Paul collides with any other wall, touching = True
tells the program that Paul has hit a tile, so that it can handle his movement and animations accordingly.
On Line 91, if the touching
variable is False
(Paul isn't touching a tile), then the program will run the if not touching:
code block.
On Line 92, MY.grounded = False
is set if Paul hasn't collided with anything. In other words, Paul is in the air and not touching the ground, a wall, or the ceiling.
On Line 95, if MY.player.collides_with(MY.exit_portal):
checks if Paul has collided with the exit portal to move to the next level or win the game.
On Line 96, the if
statement if MY.level_num >= 1 and MY.level_num < 6:
checks if the current level is between 1 and 5. If the level is 1-5, then Paul will move to the next level.
On Line 98, to move to the next level, the variable MY.level_num
is increased by one by setting it to = MY.level_num + 1
. For example, if Paul is on level 2 and enters the portal, then he would start level 3.
On Line 99, level_name_as_string = 'Level' + str(MY.level_num)
increases the level name by one as well.
On Line 100, the variable tilemap
contains the layout of the level and is loaded by using the read_file()
function. This function has the file location as its parameter to load and read a file. In this case, the parameter "Assets/" + level_name_as_string + ".txt"
is inside the function's parentheses.
On Line 101, the function load_level(tilemap)
takes the level information stored in tilemap
and updates the game to show the new level.
If Paul is currently on the last level, level #6, and he enters (collided with) the exit portal, then he has won the game! On Line 102, the statement elif MY.level_num == 6:
checks if the level number is 6.
On Line 104, the function change(2)
shows the Win screen.
On Line 107, the function update_level(delta_time)
updates all the animations and other information about the level, according to the delta_time
variable. This keeps all the animations on the level in sync.
On Line 110, Manager.register(Intro)
registers the introduction of the game. This file shows the introduction screen when you start the game.
On Line 111, Manager.register(sys.modules[__name__])
registers the name of the current file and stores that in the manager module. This file runs the main game.
On Line 112, Manager.register(Lose)
registers the game state when the player loses the game.
On Line 113, Manager.register(Win)
registers the game state when the player wins the game.
On Line 116, Manager.run(SCREEN, WINDOW, BLUE, "CHALLENGE2")
runs the game, with CHALLENGE2
set as one of the parameters, so that it knows which challenge of Level 5 you're building. This helps the init.py file know what code to run.
On Line 63, make sure you are indented at the same spot as the Line 62 comment. You want to create a for loop that checks each battery
object in your MY.batteries
list, so that you can see if Paul collides with it. (Batteries are what increase Paul's health points.)
- Start by typing the
for
keyword. - Add a space and then type
battery
on the line, which is the variable that will be assigned to the object in theMY.batteries
list. - Add a space and type the word
in
on the line. This keyword is used to check for the assigned objects within a list. - Add a space and type in the
MY.batteries
list. This list is like an array, and it holds each of the batteries' locations in variables that are saved in each spot or object within the list. - End the line with a colon, because it's a
for
loop!
The final line of code is: for battery in MY.batteries:
On Line 64, you will write an if
statement that checks to see if the player has collided with the battery. Here's how to write this statement:
- Make sure Line 64 is indented to the right of Line 63.
- Type in the
if
keyword to build out an if statement. These kind of statements ask if something is true or not, and if it is, the code under it runs! - Add a space, and then type in the
MY.player.collides_with()
function. - In the parentheses, type in the
battery
variable as the argument for the function. This takes the variable and checks whether the player collided with that specific battery. - End the line of code with a colon, because it's an
if
statement.
Here's what that line of code is going to look like: if MY.player.collides_with(battery):
On Line 65, if Paul touches a battery, then it will be removed from the MY.batteries
list, with the function remove()
. Like Line 64, battery
is the argument in this function. In this case, you use the .
to once again combine MY.batteries
and remove(battery)
to form the full function code. This line of code removes the battery from the screen, because Paul just collected it!
The full line of code is: MY.batteries.remove(battery)
On Line 66, you will write code to increase Paul's health by 1. Paul's health value is being held in the MY.player_health
variable, and you can add to his health by using the +=
operator and then typing the number 1
since you are increasing his health by 1.
- Type in the
My.player_health
variable. Make sure your code on Line 66 is aligned with the code on Line 65. - Add a space, and then add in the
+=
operator. That's a special operator that adds the number to the existing variable's number and then sets the variable to the new number. This line of code is the same as if your code wasMY.player_health = MY.player_health + 1
. - Add another space, and then type in the number
1
at the end of the line.
You're done! Run it, and make sure it works!
I included a solution file for Level 5: Creeper Chase Challenge 2. This file has all the code filled in, so if you run into any issues with your code (for example, it doesn't compile/run, or something isn't working correctly in your program), then you can take a look at the final code file to see what you did differently:
IMPORTANT: Please don't cheat yourself! Finish the game first!
Next, you can take on the other challenge to chase the Creeper and learn more! When you're done, you can move on to Level 6, the Boss Battle!
Challenge 1: If you haven't taken this challenge yet, in this one, you're going to write code that will add a time limit for 45 seconds for each stage of the game.
Bonus Challenge: In this extra challenge, you'll see how to make your own levels for Paul to run and jump (and fly) through! Practice your game design skills!
-
Level 5: Unplugged Activity - I wrote this page with more details than what you saw in the book. In this game,
-
Level 5: Rewards - If you completed the Creeper Chase program that we talked about, then I set up this page to act as a reward. You can see some illustrations of me and learn more about who I am! You'll also find the **WHAT **Award digital download, to show off your accomplishment!
-
Level 5: Creeper Chase: All Online Articles - Return back to the main Level 5 page, with all our online resources!
After you're completely done with Level 5 (did you do the challenges?), then it's time to move on to Level 6! While you read through Level 6 in your book, you can check out the resources from Paul Python, as he teaches you how to build the Boss Battle program:
I hope you had fun learning about the Creeper Chase game!
-- Intelli-Scents