Python: Castle Clash

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 89

PYTHON

MIDDLE SCHOOL EDITION

CASTLE CLASH

GAME D E VEL O PMENT GU IDE


BY PIX EL PA D
GAM E D EVEL O PMEN T G UI D E
PI XEL PAD

CASTLE CLASH
WORKBOOK
Copyright © 2021 by PixelPAD

Second Edition

EDITORS
Jamie Chang
Ivo van der Marel
Arthur Teles
Rochelle Magnaye

DESIGNERS
Fernando Medrano
Kenneth Chui
Prateeba Perumal
Emily Chow

www.pixelpad.io
www.underthegui.com
CONTENTS
COURSE GOALS &
LEARNING OUTCOMES / 07

C HAPTER 01. C H AP T ER 0 5.
/ 09 • Setting Up / 41 • Health and Attack Damage
• UI/UX Philosophy • Healthbars
• Base Health and Healthbars

C HAPTER 02. C H AP T ER 0 6.
/ 12 • Creating our Map / 51 • Game Over Screens
• Bases
• Bridges

C HAPTER 03 . C H AP T ER 0 7 .
/ 20 • Unit Cards / 60 • More Units
• Creating our First Unit • Spawning Other Enemy
• Spawning Units Units

C HAPTER 04 .
/ 30 • Unit AI
• Teams
• Enemy Spawning
EXTRA ACTIVITIES / 68

GLOSSARY / 70

CHALLENGE QUESTION / 86
COURSE GOALS &
LEARNING OUTCOMES

Students create a top-down strategy game for mobile, similar to popular


strategy games currently played on mobile devices.

Students will be able to adjust features, change their sprites and re-skin
their game to make it uniquely their own. In addition, they will be able to
create custom units, AI behaviour and multiple worlds.

7
COURSE GOALS

ű Students have the ability to create more advanced types of games,


similarto professional games on the market
ű Students finish with a game with simple artificial intelligence, that can
beplayed on a mobile device

LEARNING OUTCOMES

Computational Thinking and Algorithms


ű Students understand how to create an opponent in a game that is
controlled by simple artificial intelligence
ű Students are able to write optimized and well structured code
ű Students are able to write code making objects follow custom paths in
their game
ű Students understand and can apply loops in combination with arrays

Creativity
ű Students understand how balancing of characters, their features and their
value can improve the gameplay

Prototyping, Testing and Debugging


ű Students can reliably fix, test, and debug their own program independent
from an instructor
ű Students can develop a custom game in PixelPAD, independent from an
instructor

Construction
ű Students understand and can create games of any type, for multiple
platforms in PixelPAD

Communication & Collaboration


ű Students are able to reflect on other students code, and assist others by
showing examples and explaining programming concepts

SAMPLE PROJECT
GAME GUIDE I PREFACE

The book’s project can be found here:

https://pixelpad.io/app/juuorqvgkib/?edit=1

8
01.
C H A P TER

SETTING UP

In this workbook we’ll be making Castle Clash, a top down strategy game
that is targeted at mobile platforms!

The goal of the game is to spawn as many units as you can overwhelm the
enemy base while defending your own!

This is what your game will look like at the end!


9
First, we will need to create a new project in pixelPAD. Once you’ve logged
in you want to:

Click on the “MY PAD” button on the


left, if you don’t see it click on the
hamburger icon (three lines) next to
pixelPAD.

Then click on the blue button


“Create App”.

A window should pop-up, type in


your App Name. Feel free to name
it any way you like. Here, we named
the app “Castle Clash”.

Make sure the App Engine is


“pixelpad2D”.

Click “Create” when you’re done.


GAME GUIDE I CHAPTER ONE

10
Upon the creation of your project, you should be greeted by the PixelPAD
Development Environment.

UI/UX PHILOSOPHY

For this particular game we are “targeting” mobile platforms. This means
that we are designing and creating our game in a way that will be easy to
use on phones or other touchscreen devices!

The concept of making things easy to use and interact with is called User
Interface/ User Experience Design. UI/UX for short.

For some people, their whole job is to make things easy to use and interact
with. A few examples of some fields that heavily involve UI/UX are games,
websites, public signage,maps, and much much more.

For our game, this means thinking about what makes our game mechanics
and design easy to use for a mobile player.

As we make our game there will be small explanations whenever we make


a design or gameplay decision that takes into consideration a mobile user.

11
02.
CHA P TER

CREATING OUR MAP

The first thing we are going to add to our game is our game terrain.. For
this workbook example, we’re going to create a map that has a river that
runs through the middle.

There won’t be complex terrain in our map because on a mobile screen,


we need to have a straightforward layout so that we are able to read all
gameplay information!

1
Create a new Class
Classes
GAME GUIDE I CHAPTER TWO

Enter the script name


Background

It should show up under


Classes
Classes
12 Background
Pay attention to the pink and purple
Click on the Game Class, tabs! These tell you where to type in
then click on the Start Tab your code.
of your editor.
Add the following bolded code
below.
Game Start

self.field = Background()

Here we simply add a fruit object to our game and naming it “field”.

The self attributes the created object to the class this code is written in, in
this case, the Game class.

In plain english, this line would look like this: “field” is my object from the
class Background.

SAVE PLAY

Save and Play your game to see the changes

You’ll notice that all you can see is a box that says “Empty Image”

This is because we haven’t assigned a sprite to our background object!

13
2
Let’s add a sprite
Sprites

Find and select a sprite for


your Background

Enter the sprite name Notice how it automatically adds


.png to the end of our space
background sprite. The .png is the file type.

Note: If you are using a different


It should show up under Sprites sprite, it may be .jpg or something
Sprites else. Make sure to pay attention to
background.png the type.
GAME GUIDE I CHAPTER TWO

14
Click on the Background
Class, then click on the
Start Tab of your editor.

Background Start

self.sprite = sprite(“background.png”)

Here we assign the Background object our “background.png” sprite.

SAVE PLAY

Save and Play your game to see the changes

You should now have your background object placed in the game.

15
BASES

Now that we have a background for our game, let’s add bases. These
buildings will be where units from both sides will spawn from.

For our workbook example, we’re going to be placing bases at the left and
right of our game, directly in the middle.

1 Create a new Class


2 Now let’s add a sprite
Classes Sprites

Enter the script name Find and select a sprite for


your Base
Base

It should show up under


Classes
Classes
Base

Enter the sprite name

base

It should show up under Sprites


GAME GUIDE I CHAPTER TWO

Sprites
base.png

16
Click on the Base Class,
then click on the Start Tab
of your editor.

Base Start

self.sprite = sprite(“base.png”)

Here we are simply assigning the Base object the base sprite.

SAVE PLAY

Save and Play your game to see the changes

Now that we have our base objects, let’s add them to our game’s map.

Click on the Game Class,


then click on the Start Tab
of your editor.
Game Start

self.field = Background()

self.playerBase = Base()
self.playerBase.x = -500

self.enemyBase = Base()
self.enemyBase.x = 500

Here we add two base objects to our game, named playerBase and
enemyBase.

Then we set their positions to top the left and right of our game map. Feel
free to change the x and y position of your bases to anywhere in your
map.

SAVE PLAY

Save and Play your game to see the changes

You should now see two bases in your game, we’ll come back to them later
to add spawning and health.

17
BRIDGES

Next, we’re going to make some bridges to span the river. We’re going to
be directing our AI units to cross only where the bridges are.

1 Create a new Class


2 Now let’s add a sprite
Classes Sprites

Enter the script name Find and select a sprite for


your Bridge
Bridge

It should show up under


Classes
Classes
Bridge

Enter the sprite name

bridge

It should show up under Sprites


Sprites
bridge.png
GAME GUIDE I CHAPTER TWO

18
Bridge Start

self.sprite = sprite(“bridge.png”)

Here we are simply assigning the Bridge object the bridge sprite.

SAVE PLAY

Now that we have our object, let’s add it into our game.

Game Start
self.field = Background()

self.playerBase = Base()
self.playerBase.x = -500

self.enemyBase = Base()
self.enemyBase.x = 500

self.bridgeTop = Bridge()
self.bridgeTop.y = 200

self.bridgeBottom = Bridge()
self.bridgeBottom.y = -200

Here we add two bridge objects to our game, we then manually set their
y positions to span the river. Feel free to adjust or change where your
bridges are placed!

SAVE PLAY

You should now be able to see two bridges going across your river!

19
03.
C H A P TER

UNIT CARDS

Next we’re going to create a way to spawn units from our bases!

Since this game will be played on mobile, we are going to design around
the fact that the player will most likely not have access to a physical
keyboard.

To let the player spawn units easily, we’re going to create “card” buttons for
our player to tap.
GAME GUIDE I CHAPTER THREE

20
1 Create a new Class
2 Now let’s add a sprite
Classes Sprites

Enter the script name Find and select a sprite for


your Card
Card

It should show up under


Classes
Classes
Card

Enter the sprite name

slimeCard

It should show up under Sprites


Sprites
slimeCard.png

21
Card Start

self.sprite = sprite(“slimeCard.png”)

Here we simply assign the Card the “slimeCard.png” sprite.

SAVE PLAY

Now that we have the card object, let’s add it to our game.

Game Loop

self.field = Background()

self.playerBase = Base()
self.playerBase.x = -500

self.enemyBase = Base()
self.enemyBase.x = 500

self.bridgeTop = Bridge()
self.bridgeTop.y = 200

self.bridgeBottom = Bridge()
self.bridgeBottom.y = -200

self.slimeCard = Card()
self.slimeCard.x = -550
self.slimeCard.y = -270

Here we create the new slimeCard object as well as place it at the bottom
left of our game by directly changing its x, y position. This will let our
player tap the card without obstructing gameplay information!

SAVE PLAY
GAME GUIDE I CHAPTER THREE

22
Your spawning card should now show up in the bottom left corner of your
game!

CREATING OUR FIRST UNIT

Although we have our spawning card, we need to make the unit that will be
created whenever we tap on the card.

This unit should play an animation from a special type of sprite, a


spritesheet! A sprite sheet is a group of images all together in one large
image, like this:

23
1 Create a new Class
2 Now let’s add a sprite
Classes Sprites

Enter the script name Find and select a sprite for


your Unit
Unit

It should show up under


Classes
Classes
Unit

Enter the sprite name

slimeUnit

It should show up under Sprites


Sprites
slimeUnit.png
GAME GUIDE I CHAPTER THREE

24
We should first create the sprite sheet and slice it into smaller sprites. To
slice it, we have to know how many rows and columns this sprite has. Let’s
open the slimeUnit.png sprite.

Sprites
slimeUnit.png

As you can see, our slime animation has 8 images. They are divided into 1
row and 8 columns. That’s all the information we need from this sprite for
now.

Unit Start

spriteSheet = sprite(“slimeUnit.png”,1,8)
anim = animation(spriteSheet, 20, 0, 7)
animation_set(self, anim)

Here we are creating a new sprite sheet from our ‘slimeUnit.png’ sprite. In
our example, our sprite has 1 row and 8 columns.

Next we create an animation that takes our sprite sheet, runs it at 20


frames per second, and loops over the sprite number 0 to the sprite
number 7 (8 images in total, so it loops over the whole sprite sheet).

We then set that animation on our current class, the Unit.

SAVE PLAY

25
SPAWNING UNITS

Now that we have a spawning card as well as a unit to spawn, we now need
to make sure that card creates unit objects.

To detect if we are tapping the card, we are going to need an object that
keeps following our mouse. Once we tap on the screen, we check if this
object is colliding with a card. If it is, we then spawn a unit.

1 Create a new Class


2 Now let’s add a sprite
Classes Sprites

Enter the script name Find and select a sprite for


your HealthPickup
Cursor

It should show up under


Classes
Classes
Cursor

Enter the sprite name

mouseCollider
GAME GUIDE I CHAPTER THREE

It should show up under Sprites


Sprites
mouseCollider.png

Colliders are what make our objects collide with each other. In pixelPAD,
colliders are simply sprites. We are going to use this to check where the
player is clicking by checking collisions between our Cursor class and other
objects in our game, like cards.
26
Cursor Start

self.sprite = sprite(“mouseCollider.png”)

Here we simply assign the Cursor class the ‘mouseCollider.png’ sprite.

Now that we have created our Cursor object, let’s add it to our game.

Game Loop

...

self.bridgeBottom = Bridge()
self.bridgeBottom.y = -200

self.slimeCard = Card()
self.slimeCard.x = -550
self.slimeCard.y = -270

self.pointer = Cursor()

Here we are simply adding a Cursor object to our game, and naming it
“pointer”.

SAVE PLAY

You should now be able to see your cursor’s collider right in the middle of
your screen. However, as this object is what is going to detect where we are
clicking, it should always be in the same position of our mouse.

27
Cursor Loop

self.x = mouse_x()
self.y = mouse_y()

Here we keep continuously updating the Cursor object’s position by


changing its x and y coordinates so it always matches our mouse’s
position.

SAVE PLAY

Your cursor collider should now be following your mouse around the game
screen. Now, the last step is to be able to detect clicks on the card and
spawn a unit if that happens.

Cursor Loop

self.x = mouse_x()
self.y = mouse_y()

if mouse_was_pressed(“left”):
cardClicked = get_collision(self, “Card”)
if cardClicked:
slime = Unit()
slime.x = game.playerBase.x
slime.y = game.playerBase.y

First, we check if the left mouse button was pressed.

Next, we use the variable cardClicked to store the return value of the
function get_collision

The get_collision function will return the first Card object colliding with the
Cursor. If there is no collision, then the function will return false.

If it is colliding with a card, then we create a new slime object, and set its
position to the playerBase location.

SAVE PLAY
GAME GUIDE I CHAPTER THREE

You should now notice that your card spawns units at your player’s base!

28
Now that we’ve made sure that our cursor is working correctly, we can hide
it from the game so we don’t have a flying red dot following our mouse. We
can simply make it invisible by setting it’s visible variable to false.

Cursor Start

self.sprite = sprite(“mouseCollider.png”)

self.visible = False

Here, we simply set this object to be invisible by setting its visible


variable’s value to false.

SAVE PLAY

29
04.
CHA P TER

UNIT AI

Next, we’re going to need to make our units move on their own. However,
we can’t simply move them up. We’re going to need them to cross bridges
to get to the enemy base as well as pick a bridge to cross.

Let’s start by adding some necessary variables to the slime unit.

Unit Start

spriteSheet = sprite(“slimeUnit.png”,1,8)
anim = animation(spriteSheet, 20, 0, 7)
animation_set(self, anim)
GAME GUIDE I CHAPTER FOUR

self.destinationX = 0
self.destinationY = 0

Here we are creating variables that can store our x and y position of the
destination the unit should move to.

SAVE PLAY

30
Next, we need to use this variable to determine how we are going to move
the unit.

Unit Loop

if self.x < self.destinationX:


self.x = self.x + 1
elif self.x > self.destinationX:
self.x = self.x - 1

if self.y < self.destinationY:


self.y = self.y + 1
elif self.y > self.destinationY:
self.y = self.y - 1

Here we have a series of ifs and else ifs.

First we check if the current x position of the unit is less than the
destinationX position.

If it is, then we increase the x position of the unit, making it move to the
right.

Otherwise, if our x position is greater than the destinationX position, then


we decrease the x position of the unit, making it move to the left.

We repeat the same code afterwards, except now we are comparing the
current y position of the unit to the destinationY position.

SAVE PLAY

You will notice that your units are walking to the right, but they stop at the
middle of your game. That’s because destinationX and destinationY are
equal to 0. Let’s go ahead and make our unit randomly choose one of the
two bridges as its first destination.

31
Unit Start

import random

spriteSheet = sprite(“slimeUnit.png”,1,8)
anim = animation(spriteSheet, 20, 0, 7)
animation_set(self, anim)

self.destinationX = 0
self.destinationY = 0

self.bridgeSelected = random.randint(1,2)

if self.bridgeSelected == 1:
self.destinationX = game.bridgeTop.x
self.destinationY = game.bridgeTop.y
else:
self.destinationX = game.bridgeBottom.x
self.destinationY = game.bridgeBottom.y

First, at the top of our code, we import the random library.

This allows us to use python functions that let us generate random


numbers.

Next we create a variable that is assigned to a random integer between 1


and 2, which gives us two possible results that will help us pick a bridge to
cross.

Afterwards, we have an if statement that checks whether or not


bridgeSelected has been evaluated to 1 or 2.

Depending on the number picked, we assign the destinationX and


destinationY variables of the kid unit to either bridge’s x,y position.

SAVE PLAY

Your units should now pick a bridge to go to randomly!


GAME GUIDE I CHAPTER FOUR

32
As you can see in our example, our slimes are moving backwards. We can
easily flip their sprites by setting their scaleX value to -1. You might not
need to do it in your project if your object’s sprite is already facing the right
direction.

Every object has default scaleX and scaleY values set to 1. If we invert the
scaleY value, we flip the object vertically, and if we invert the scaleX value,
we flip it horizontally.

Cursor Loop

self.x = mouse_x()
self.y = mouse_y()

if mouse_was_pressed(“left”):
cardClicked = get_collision(self, “Card”)
if cardClicked:
slime = Unit()
slime.x = game.playerBase.x
slime.y = game.playerBase.y
slime.scaleX = -1

Here we simply flip the slime horizontally by setting its scaleX value to -1
after its creation.

SAVE PLAY

33
TEAMS

When you play your game, you may notice that your units bunch up on the
bridges.

This is because once your units arrive at their destination, they don’t assign
themselves a base to go to!

Unit Start

...

if self.bridgeSelected == 1:
self.destinationX = game.bridgeTop.x
self.destinationY = game.bridgeTop.y
else:
self.destinationX = game.bridgeBottom.x
self.destinationY = game.bridgeBottom.y

self.team = None

Here we are creating a variable to store the team of the unit. At the
moment we assign this value to none, but using the bases, we can assign
this externally.

SAVE PLAY

Now that we have a team variable, we need to assign it a value when we


spawn one using the card.

Cursor Loop

self.x = mouse_x()
self.y = mouse_y()

if mouse_was_pressed(“left”):
GAME GUIDE I CHAPTER FOUR

cardClicked = get_collision(self, “Card”)


if cardClicked:
slime = Unit()
slime.x = game.playerBase.x
slime.y = game.playerBase.y
slime.scaleX = -1
slime.team = “player”

Now, when we create a new slime object, we assign its team as well.

SAVE PLAY
34
Next, we need to head back into the Unit class to make use of this new
variable.

Unit Loop

if self.x < self.destinationX:


self.x = self.x + 1
elif self.x > self.destinationX:
self.x = self.x - 1

if self.y < self.destinationY:


self.y = self.y + 1
elif self.y > self.destinationY:
self.y = self.y - 1

if self.x == self.destinationX and self.y == self.


destinationY:
if self.team == “player”:
self.destinationX = game.enemyBase.x
self.destinationY = game.enemyBase.y
else:
self.destinationX = game.playerBase.x
self.destinationY = game.playerBase.y

Here we are checking if the current position of the unit is the same as the
destination’s position.

If it is, we then check what team the unit is on. If the unit’s team is ‘player’,
then we set the destination to be the enemy base.

If the team isn’t ‘player’, then we set the destination to the player’s base.

SAVE PLAY

You should now notice that your units move to the bridge, and then to the
enemy base!

35
ENEMY SPAWNING

Now that our units move to the enemy base, we need to let our enemy AI
retaliate as well.

First let’s handle how the enemy will assign teams. We need to create a
team variable for the bases so that they can assign the right team to its
units.

Base Start

self.sprite = sprite(“base.png”)

self.team = None

Just as we assigned no team to the unit, we create a variable for a base’s


team with the value of none.

SAVE PLAY

Next, we need to give the bases we already have a team.

Game Start
self.field = Background()

self.playerBase = Base()
self.playerBase.x = -500
self.playerBase.team = “player”

self.enemyBase = Base()
self.enemyBase.x = 500
self.enemyBase.team = “enemy”

self.bridgeTop = Bridge()
self.bridgeTop.y = 200

self.bridgeBottom = Bridge()
GAME GUIDE I CHAPTER FOUR

self.bridgeBottom.y = -200

self.slimeCard = Card()
self.slimeCard.x = -550
self.slimeCard.y = -270

self.pointer = Cursor()

For each of the base objects in our scene we assign the team variable to
either ‘player’ or ‘enemy’.

36
SAVE PLAY
Now that these bases have teams associated with them, let’s let bases
automatically spawn units.

First, we need to find a sprite to represent our enemy’s units. For our
example we are going to be using ghosts as enemies!

Now let’s add a sprite


Sprites

Find and select a sprite for


your Ghost

Enter the sprite name

ghostUnit

It should show up under Sprites


Sprites
ghostUnit.png

37
Next, we’ll need to create a spawnTimer in our Base class, so that our base
doesn’t spawn a unit every frame.

Base Start

self.sprite = sprite(“base.png”)

self.team = None

self.spawnTimer = 0

Here we simply create a variable that stores the amount of frames that
have passed. We’ll use this variable to delay spawning a new unit.

SAVE PLAY

Now that we have our timer variable, let’s use it in Loop.

Base Loop

if self.team == “enemy”:
self.spawnTimer = self.spawnTimer + 1
if self.spawnTimer >= 60:
enemyUnit= Unit()
enemyUnit.team = self.team
spriteSheet = sprite(“ghostUnit.png”,1,8)
anim = animation(spriteSheet, 20, 0, 7)
animation_set(enemyUnit, anim)
enemyUnit.x = self.x
enemyUnit.y = self.y
self.spawnTimer = 0

First, we check if this base is from the team ‘enemy’. If so, we add one to
the spawnTimer’s current value every frame.

Next, we check if 60 frames have passed (around a second).


GAME GUIDE I CHAPTER FOUR

If the condition is true, we then create a new unit named enemyUnit, set
its team to the current team of the base, set the ghost animation on it, and
position it on top of the base.

Next, we set the spawnTimer back to 0 so that we can repeat this code in
another 30 frames.

SAVE PLAY

38
You should now notice that your enemy base spawns units back towards
you! However, both the player’s and enemy’s units change their direction as
soon as they reach the middle of the bridge, instead of finishing crossing
the bridge first. That makes them walk on the water.

To fix that, we can check if our units have walked enough to cross the
bridge before changing their destination to the other base. We can look at
the units’ x position to get that information.

The units should walk 250 pixels past the middle of the screen to cross the
bridge. For the player’s units, we can check if their position is greater than
250. However, for the enemy’s units, their position has to be smaller than
-250 as they’re moving to the left.

39
Unit Loop

...

if self.y < self.destinationY:


self.y = self.y + 1
elif self.y > self.destinationY:
self.y = self.y - 1

if self.x == self.destinationX and self.y == self.


destinationY:
if self.team == “player”:
if self.x == 250:
self.destinationX = game.enemyBase.x
self.destinationY = game.enemyBase.y
else:
self.destinationX = 250
else:
if self.x == -250:
self.destinationX = game.playerBase.x
self.destinationY = game.playerBase.y
else:
self.destinationX = -250

Here we are modifying our if statements to check if the player unit is at the
x position of 250 before changing its destination to the enemy base. If it
isn’t, we set it’s destinationX to 250.

We then do the same with the enemy units, but checking for the position
-250, as they’re moving to the left.

SAVE PLAY

You should now notice that all units cross the bridge entirely before moving
to the other base!
GAME GUIDE I CHAPTER FOUR

40
05.
C H A P TER

HEALTH AND ATTACK DAMAGE

Now that we can have units going back and forth, we need to add health to
our units so the balance of power can shift!

Unit Start

...

if self.bridgeSelected == 1:
self.destinationX = game.bridgeTop.x
self.destinationY = game.bridgeTop.y
else:
self.destinationX = game.bridgeBottom.x
self.destinationY = game.bridgeBottom.y

self.team = None

self.health = 100
self.attack = 5

Here we simply create a variable for health and give it a base value of 100

Later we create another variable for attack and give it a base value of 5.

SAVE PLAY

41
Now that we have a value for health, we’re going to need some way to
check for collisions between different units. Let’s handle collisions between
units.

Unit Loop

...

if self.x == self.destinationX and self.y == self.


destinationY:
if self.team == “player”:
if self.x == 250:
self.destinationX = game.enemyBase.x
self.destinationY = game.enemyBase.y
else:
self.destinationX = 250
else:
if self.x == -250:
self.destinationX = game.playerBase.x
self.destinationY = game.playerBase.y
else:
self.destinationX = -250

otherUnits = get_collision_list(self, “Unit”)


if otherUnits:
for unit in otherUnits:
if unit.team != self.team:
unit.health = unit.health - self.attack

Here are a bunch of nested statements, let’s break them down.

First we check for collisions between the current Unit and all other “Unit”
objects.

get_collision_list returns something called an array. For an in-depth


explanation of arrays, head to the Python Basics section of the glossary!

Next, we have a for each loop. The for each goes through every object that
is returned by collision_check_all and checks the team of each object.

Next we check if the collision happened.

In case we detect a collision, we then check if the other unit’s team is


different from this unit’s team. If so, that means they’re enemies.
GAME GUIDE I CHAPTER FIVE

Then, the next step is to deal damage to the other unit by decreasing their
health values with our attack value.

SAVE PLAY

42
Although we’ve created a health system, our units don’t despawn once they
hit 0 health!

Unit Loop

...

if self.x == self.destinationX and self.y == self.


destinationY:
if self.team == “player”:
if self.x == 250:
self.destinationX = game.enemyBase.x
self.destinationY = game.enemyBase.y
else:
self.destinationX = 250
else:
if self.x == -250:
self.destinationX = game.playerBase.x
self.destinationY = game.playerBase.y
else:
self.destinationX = -250

otherUnits = get_collision_list(self, “Unit”)


if otherUnits:
for unit in otherUnits:
if unit.team != self.team:
unit.health = unit.health - self.attack

if self.health <= 0:
destroy(self)

Here we added a simple if statement checking if our health has fallen


below 0.

If the unit’s health has fallen below 0, then we destroy the unit.

SAVE PLAY

You should notice that your units start disappearing once their health hits 0!

43
HEALTHBARS

Although we have health for our units, it would be really useful to see the
health of our units at a glance. To do this, we’re going to create healthbars.

1 Create a new Class


2 Now let’s add a sprite
Classes Sprites

Enter the script name Find and select a sprite for


your HealthBar
HealthBar

It should show up under


Classes
Classes
HealthBar

Enter the sprite name

healthBar

It should show up under Sprites


Sprites
healthBar.png
GAME GUIDE I CHAPTER FIVE

44
HealthBar Start

self.sprite = sprite(“healthBar.png”)

Here we simply assign the HealthBar class the sprite “healthBar.png”

SAVE PLAY

Now that we have a HealthBar object, we’re going to need to give every
unit we spawn a HealthBar, as well as scale it to the current health of the
unit.

Unit Start

...

if self.bridgeSelected == 1:
self.destinationX = game.bridgeTop.x
self.destinationY = game.bridgeTop.y
else:
self.destinationX = game.bridgeBottom.x
self.destinationY = game.bridgeBottom.y

self.team = None

self.health = 100
self.maxHealth = self.health
self.attack = 5

self.healthBar = HealthBar()

Here we’ve added two lines.

First we create a new healthBar object. This is our solid red rectangle from
earlier.

Next we have a variable that stores the maximum value of health. Since
this code is in start, we can simply set it to health.

SAVE PLAY

45
Now that we have our variables, let’s edit this code in Loop.

Unit Loop

...

if self.x == self.destinationX and self.y == self.


destinationY:
if self.team == “player”:
if self.x == 250:
self.destinationX = game.enemyBase.x
self.destinationY = game.enemyBase.y
else:
self.destinationX = 250
else:
if self.x == -250:
self.destinationX = game.playerBase.x
self.destinationY = game.playerBase.y
else:
self.destinationX = -250

otherUnits = get_collision_list(self, “Unit”)


if otherUnits:
for unit in otherUnits:
if unit.team != self.team:
unit.health = unit.health - self.attack

self.healthBar.y = self.y - 40
self.healthBar.x = self.x
self.healthBar.scaleX = self.health / self.maxHealth

if self.health <= 0:
destroy(self.healthBar)
destroy(self)

Here we set the position of the healthBar slightly below the unit on every
frame.

Next, we set the scaleX of the sprite to a fraction of the original health.

We do this by dividing our current health by our maximum health, which


gives us a percentage of how much health we have left. With that we can
accurately scale the healthBar as needed!
GAME GUIDE I CHAPTER FIVE

Finally, we make the unit also destroy its lifebar if it’s life reaches zero.

SAVE PLAY

46
You might now have health bars appearing in the middle of your game
right when a unit is spawned in game. That happens because it takes some
time from when the health bar is created till when it is moved to below
the unit. We can simply solve that problem by making the health bar to be
created outside of the screen.

HealthBar Start

self.sprite = sprite(“healthBar.png”)

self.x = 999

Here we make the health bar to spawn outside of the game screen by
changing its x position.

SAVE PLAY

You should now see that health bars of units shrink until they are destroyed!

47
BASE HEALTH AND HEALTHBARS

Let’s add some health and health bars to our bases now!

Base Start

self.sprite = sprite(“base.png”)

self.team = None

self.spawnTimer = 0

self.health = 100
self.maxHealth = self.health

self.healthBar = HealthBar()

Here we create a health variable as well as a maxHealth variable.

After that, just like the unit, we create a new healthBar object.

SAVE PLAY

Next, we need to update these values in Base’s loop tab.

Base Loop

if self.team == “enemy”:
self.spawnTimer = self.spawnTimer + 1
if self.spawnTimer >= 60:
slime = Unit()
slime.team = self.team
slime.sprite = sprite(“slimeUnit.png”)
slime.x = self.x
slime.y = self.y
self.spawnTimer = 0

self.healthBar.y = self.y - 120


GAME GUIDE I CHAPTER FIVE

self.healthBar.x = self.x
self.healthBar.scaleX = self.health / self.maxHealth

Here we’re placing the healthBar slightly below the base just like our units.

Afterwards, we change the scaleX of the healthBar using the same


calculation that we used on the unit’s healthBar.

SAVE PLAY

48
You may have noticed that your base health hasn’t gone down despite your
units colliding with it!

That’s because we haven’t added the collision check to our units. Let’s do
that now.

Unit Loop

...

otherUnits = get_collision_list(self, “Unit”)


if otherUnits:
for unit in otherUnits:
if unit.team != self.team:
unit.health = unit.health - self.attack

otherBase = get_collision(self, “Base”)


if otherBase:
if otherBase.team != self.team:
otherBase.health = otherBase.health -
self.attack

self.healthBar.y = self.y - 40
self.healthBar.x = self.x
self.healthBar.scaleX = self.health / self.maxHealth

if self.health <= 0:
destroy(self.healthBar)
destroy(self)

Here we add a get_collision code that checks for collisions between the
unit and any “Base” object.

If there is a collision with a base that does not have the same team as the
unit, deal damage to the base. We also set this unit’s life to 0 so it gets
destroyed.

SAVE PLAY

49
Your bases should now decrease health and be destroyed after their health
reaches 0!
GAME GUIDE I CHAPTER FIVE

50
06.
C H A P TER

GAME OVER SCREENS

Now that our bases are already taking damage from units, we need to
create game over screens for when the player wins and loses the game.

Create a new Room


Rooms

Enter the room name


YouWin

It should show up under


Rooms
Rooms
YouWin

51
YouWin Start

self.youWinText = text(“You Win”, -440, 130)


self.youWinText.color = “green”
self.youWinText.fontSize = 250

Here we first create a text object that says “You Win”, and change its x
position to -440 and y position to 130.

Later, we change this text color to green.

Finally, we increase the font size to 250.

SAVE PLAY

Create a new Room


Rooms

Enter the room name


YouLose

It should show up under


Rooms
Rooms
YouLose

YouLose Start

self.youLoseText = text(“You Lose”, -500, 130)


GAME GUIDE I CHAPTER SIX

self.youLoseText.color = “red”
self.youLoseText.fontSize = 250

Here we first create a text object that says “You Lose”, and change its x
position to -500 and y position to 130.

Later, we change this text color to red.

Finally, we increase the font size to 250.

52
Now that we have both rooms created, we are going to redirect the player
to one of them in case he wins or loses the game.

To go to the YouWin room, the enemy base needs to be destroyed. On


the other hand, to go to the YouLose room, the player base needs to be
destroyed.

Base Start

if self.team == “enemy”:
self.spawnTimer = self.spawnTimer + 1
if self.spawnTimer >= 60:
slime = Unit()
slime.team = self.team
slime.sprite = sprite(“slimeUnit.png”)
slime.x = self.x
slime.y = self.y
self.spawnTimer = 0

self.healthBar.y = self.y - 120


self.healthBar.x = self.x
self.healthBar.scaleX = self.health / self.maxHealth

if self.health <= 0:
if self.team == “enemy”:
set_room(“YouWin”)
else:
set_room(“YouLose”)

Here we first check if the base’s health has reached 0.

Next, we check if the base’s team is ‘enemy’. If so, we set the YouWin room.
If not, we set the “YouLose” room.

SAVE PLAY

You should now get different screens in case you win or lose the game. Feel
free to style these screens as you like!

53
Now that we have our game over rooms, we should be able to replay our
game without stopping and playing our game through PixelPAD. That can
be done by running our game in a Room instead of running it straight on
the Game class. Then, we can just reload the room to play the game again.

For that, we will have to move some code around.

Create a new Room


Rooms

Enter the room name


Play

It should show up under


Rooms
Rooms
Play

Open the Game Class and go to the Start tab.

Highlight all the code with your mouse, and copy it with Ctrl+C

Open the Play Class and go to the Start tab.

Paste your code that you copied back in the Game class with Ctrl+V
GAME GUIDE I CHAPTER SIX

Delete all the code in Game Class once you’ve finished

Game Start

We copied all the code from here into the Play room. Then, we deleted all
the code from the Game class.

54
Play Start

self.field = Background()

self.playerBase = Base()
self.playerBase.x = -500
self.playerBase.team = “player”

self.enemyBase = Base()
self.enemyBase.x = 500
self.enemyBase.team = “enemy”

self.bridgeTop = Bridge()
self.bridgeTop.y = 200

self.bridgeBottom = Bridge()
self.bridgeBottom.y = -200

self.slimeCard = Card()
self.slimeCard.x = -550
self.slimeCard.y = -270

self.pointer = Cursor()

We copied all the code from the Game class into here. Then, we deleted
all the code from the Game class.

SAVE PLAY

Your game screen should now be all black since we don’t have any code
inside our Game class anymore. Now, we want to tell our game to run the
Play room as soon as the game starts.

Game Start

set_room(“Play”)

Here, we are telling our game to set the Play room as the current room.

SAVE PLAY

55
Now your game will show up on the screen. However, after a couple
seconds it causes many errors.

As we’ve copied all our code from the Game class into the Play room,
objects like our bridges, bases, and cards are now owned by the room. That
causes problems when we try to access them.

For example, our units need to know both bridges and bases in order to
move. When they look for those objects in the game class (with game.
bridgeTop for example), they can’t find it.

To solve this problem, we have to tell our game that all those objects in the
room are actually owned by the Game class, and not by the room. To do
that, we just have to change the self for game.
GAME GUIDE I CHAPTER SIX

56
Play Start

game.field = Background()

game.playerBase = Base()
game.playerBase.x = -500
game.playerBase.team = “player”

game.enemyBase = Base()
game.enemyBase.x = 500
game.enemyBase.team = “enemy”

game.bridgeTop = Bridge()
game.bridgeTop.y = 200

game.bridgeBottom = Bridge()
game.bridgeBottom.y = -200

game.slimeCard = Card()
game.slimeCard.x = -550
game.slimeCard.y = -270

game.pointer = Cursor()

Here, we are specifying that all these objects created are owned by the
Game class.

SAVE PLAY

Your game should now be working again. However, nothing has changed
yet. Now, we are going to add some code on YouLose and YouWin rooms
to reload the game if we press the left mouse button (or tap on the screen
for mobile platforms).

57
YouLose Start

self.youLoseText = text(“You Lose”, -500, 130)


self.youLoseText.color = “red”
self.youLoseText.fontSize = 250

self.replayText = text(“Click to play again”,-200,-250)


self.replayText.color = “white”
self.replayText.fontSize = 50

Here we first create a text object that says “Click to play again”, and change
its x position to -200 and y position to -250.

Later, we change this text color to white.

Finally, we increase the font size to 50.

SAVE PLAY

YouLose Loop

if mouse_was_pressed(“left”):
set_room(“Play”)

Here we first check if the left mouse button was pressed. If so, we then set
the Play room as the current room, which will restart the game.

SAVE PLAY

You should now be able to replay your game by pressing the Space key in
the YouLose room. Now you will have to repeat the same process for your
YouWin room!
GAME GUIDE I CHAPTER SIX

58
YouWin Start

self.youWinText = text(“You Win”, -440, 130)


self.youWinText.color = “green”
self.youWinText.fontSize = 250

self.replayText = text(“Click to play again”,-200,-250)


self.replayText.color = “white”
self.replayText.fontSize = 50

Here we first create a text object that says “Click to play again”, and change
its x position to -200 and y position to -250.

Later, we change this text color to white.

Finally, we increase the font size to 50.

SAVE PLAY

YouWin Loop

if mouse_was_pressed(“left”):
set_room(“Play”)

Here we first check if the left mouse button was pressed. If so, we then set
the Play room as the current room, which will restart the game.

SAVE PLAY

Your game should be totally replayable now!

59
07.
C H A P TER

MORE UNITS

Now that we have a functioning push/pull gameplay system with our units,
it’s time to diversify our unit roster by adding different units!

For our example, we’re going to be adding another unit. However, you can
change the name, sprite or gameplay behaviour of your unit.
GAME GUIDE I CHAPTER SEVEN

60
1 Let’s add a sprite
2 Let’s add another sprite
Sprites Sprites

Find and select the sprite for Find and select a sprite for
your Card your Unit

Enter the sprite name Enter the sprite name


golemCard golemUnit

It should show up under Sprites It should show up under Sprites


Sprites Sprites
golemCard.png golemUnit.png

61
Play Start

game.field = Background()

game.playerBase = Base()
game.playerBase.x = -500
game.playerBase.team = “player”

game.enemyBase = Base()
game.enemyBase.x = 500
game.enemyBase.team = “enemy”

game.bridgeTop = Bridge()
game.bridgeTop.y = 200

game.bridgeBottom = Bridge()
game.bridgeBottom.y = -200

game.slimeCard = Card()
game.slimeCard.x = -550
game.slimeCard.y = -270

game.golemCard = Card()
game.golemCard.sprite = sprite(“golemCard.png”)
game.golemCard.x = -400
game.golemCard.y = -270

game.pointer = Cursor()

Here we simply create the golemCard object from the Card class, change
its sprite to the golemCard.png sprite we just added, and move it to the
right of the slime card

SAVE PLAY
GAME GUIDE I CHAPTER SEVEN

62
Card Start

self.sprite = sprite(“slimeCard.png”)

self.spawns = None

Here we simply create the golemCard object from the Card class, change
its sprite to the golemCard.png sprite we just added, and move it to the
right of the slime card.

SAVE PLAY

Play Start

...

game.bridgeBottom = Bridge()
game.bridgeBottom.y = -200

game.slimeCard = Card()
game.slimeCard.x = -550
game.slimeCard.y = -270

game.golemCard = Card()
game.golemCard.sprite = sprite(“golemCard.png”)
game.golemCard.x = -400
game.golemCard.y = -270

game.pointer = Cursor()

Here we simply create the golemCard object from the Card class, change
its sprite to the golemCard.png sprite we just added, and move it to the
right of the slime card.

Next, we need to spawn the correct unit depending on the spawns variable
assigned to the card.

63
Cursor Loop

self.x = mouse_x()
self.y = mouse_y()

if mouse_was_pressed(“left”):
cardClicked = get_collision(self, “Card”)
if cardClicked:
if cardClicked.spawns == “slime”:
slime = Unit()
slime.x = game.playerBase.x
slime.y = game.playerBase.y
slime.team = “player”
slime.scaleX = -1
elif cardClicked.spawns == “golem”:
golem = Unit()
spriteSheet = sprite(“golemUnit.png”,1,8)
anim = animation(spriteSheet, 20, 0, 7)
animation_set(golem, anim)
golem.x = game.playerBase.x
golem.y = game.playerBase.y
golem.team = “player”
golem.scaleX = -1

Here, after checking if we’ve clicked on a card, we then check what this
card spawns. If it spawns a slime, then we just spawn the slime unit as we
were already doing before.

If the card spawns a golem, we create another Unit object called golem,
set the golem’s animation to the object, position the golem on the player
base, set it to the player team, and flip its sprite horizontally so it faces
right.

SAVE PLAY

Now, by tapping or clicking your new unit card, you can spawn another
type of unit from your base!
GAME GUIDE I CHAPTER SEVEN

64
SPAWNING OTHER ENEMY UNITS

Now that you can add other units, we can also incorporate these new units
into our enemy base spawner.

Now let’s add a sprite


Sprites

Find and select a sprite for


your Unit

Enter the sprite name

batUnit

It should show up under Sprites


Sprites
batUnit.png

65
Base Loop

import random

if self.team == “enemy”:
self.spawnTimer = self.spawnTimer + 1
if self.spawnTimer >= 60:
enemyUnit = Unit()
enemyUnit.team = self.team
spriteSheet = sprite(“ghostUnit.png”,1,8)
anim = animation(spriteSheet, 20, 0, 7)
animation_set(enemyUnit, anim)
enemyUnit.x = self.x
enemyUnit.y = self.y

unitSelected = random.randint(1,2)

if unitSelected == 1:
spriteSheet = sprite(“ghostUnit.png”,1,8)
anim = animation(spriteSheet, 20, 0, 7)
animation_set(enemyUnit, anim)
else:
spriteSheet = sprite(“batUnit.png”,1,8)
anim = animation(spriteSheet, 20, 0, 7)
animation_set(enemyUnit, anim)

self.spawnTimer = 0

self.healthBar.y = self.y - 120


self.healthBar.x = self.x
self.healthBar.scaleX = self.health / self.maxHealth

if self.health <= 0:
if self.team == “enemy”:
set_room(“YouWin”)
else:
set_room(“YouLose”)

First, we import the random library to access random number generation.

Next, we removed the old animation code we had and created a variable
that is assigned to a random number.

Based off the result of the random number we either spawn a slimeUnit or
GAME GUIDE I CHAPTER SEVEN

batUnit.

SAVE PLAY

Your enemy base should now spawn your new unit as well! If you ever want
to spawn more units from your enemy base, increase the maximum number
that can be generated by random.randint() and add a new if statement
checking for that number. In that if statement simply create a new object!

66
Congratulations! You’ve created a top-down RTS targeted at mobile
platforms!

Feel free to add more units with different behaviour or destinations, change
the layout of your map, whatever you want!

67
EXTRA ACTIVITIES

The following activities are optional and should be added to your current
game. Most of them can be added during your game’s development, but
some might require your game to be already completed. You can check the
prerequisite chapters beside the activities to know if you are able or not to
do it at the stage you are now in the course.

# Prerequisite Activity

1 Chapter 2 Change your enemy base’s sprite so it looks


different from the player base

2 Chapter 5 Make it so each unit will have a different


attack damage.
For example:
ű Spiders and slimes have 1 attack
damage
ű Ghosts and bats have 3 attack
damage

3 Chapter 5 Make it so each unit will have a random


amount of health when spawned.
For example:
ű Spiders and slimes can have from 90
GAME GUIDE I EXTRA ACTIVITIES

to 120 health
ű Ghosts and bats can have from 100
to 150 health

4 Chapter 5 Add mana (elixir) to your game


ű You should get one extra mana point
every half a second
ű You should be able to store a
maximum of 20 mana points
ű Each of your cards should cost you X
amount of mana
68
5 Chapter 6 Add a Main Menu room to your game

6 Chapter 6 Add backgrounds to your YouWin and


YouLose rooms

7 Chapter 6 Add sounds to your game

8 Chapter 7 Add a Card that spawns 2 units. One of the


units should go to the top bridge, and the
other one to the bottom bridge

69
GLOSSARY

WHAT IS PIXELPAD?
PixelPAD is an online platform we will be using to create our own apps or
games!

The PixelPAD IDE is composed of 4 areas:

ASSETS: Your assets are where you can add and access your classes and
sprites. Classes are step-by-step instructions that are unique to the object.
For example, the instructions for how your player moves will be different
from the way your asteroid moves! Sprites is another word for image, and
these images give your objects an appearance!

CODE: In this section, you will write instructions for your game. To write
your code, click within the black box and on the line you want to type on. To
make a new line, click the end of the previous line and then press “Enter”
GAME GUIDE I GLOSSARY

on your keyboard.

STAGE: The stage is where your game will show up after you write your
code and click Play (or Stop and then Play). Don’t forget to click save after
you make changes to your code!

CONSOLE: Your console is where you will see messages when there are
errors in your code, and also where you can have messages from your
game show up such as the score, or instructions on how to play your game.
70
SCRIPTS
Scripts and Assets
Two of the asset types, rooms and classes, are script assets. Script assets (or
scripts) are assets that have code inside them. Sprites are not considered
scripts, because they do not contain any code.

Creating Scripts and Assets: To Create an asset, you start by clicking the +
next to “Rooms”, “Classes” or “Sprites”

Then type in any name you’d like. My particular convention looks like this:

ű “MainTown” -> room


ű “Player” -> player class
ű “background” -> background sprite
ű “stepGrass” -> steps sound effect

Classes and rooms (scripts) should follow the “TitleCase” standard, where
all words are capitalized. For sprites and sounds (assets) we use the
“camelCase” standard, where the first word is lowercase, and every word
that follows is capitalized. This isn’t necessary, but keeps your code neat
and readable.

The Game Class


There is one class that always exists in every project: the game class. The
purpose of the game class is to load all of the other assets in our project.
The game class represents our entire game.

71
DEFAULT OBJECT PROPERTIES
Sprite, scaleX and scaleY
Every class inherits default properties when created in PixelPAD. The First of
these few properties you should learn about are:

.sprite, .scaleX, and .scaleY

.sprite is the image of the object. The value of .sprite is an image object
which we will get to later. .scaleY takes a float between 0 and 1 and
stretches the sprite of the object lengthwise. .scaleX takes a float between 0
and 1 and stretches the sprite of the object widthwise.

X, Y and Z Coordinates
The position of an object is where the object is. In programming, we usually
describe an object’s position using a pair of numbers: its X coordinate and
its Y coordinate.

An object’s X coordinate tells us where the object is horizontally (left and


right), and its Y coordinate tells us where the object is vertically (up and
down).

[0,0] is the middle of the screen

ű .x takes the value of the x position of the object. The higher .x is, the
farther to the right it is.
ű .y takes the value of the y position of the object. The higher .y is, the
higher up the object is.
ű .z takes the value of the z position of the object. The higher .z is, the
closer to you the object is.

DOT NOTATION AND SELF


Dot Notation
Dot notation is like an apostrophe s (‘s). Like “Timmy’s ball” or “Jimmy’s
shoes”.

The ‘s tells you who you’re talking about. In code instead of using
apostrophes we use dots to talk about ownership.

So when we say player.x we’re really saying “player’s x”


GAME GUIDE I GLOSSARY

Examples of Dot Notation:

ű player.x -> refers to the player’s x value


ű player.scaleX -> refers to player’s x scale (percentage)

72
The “Self” Property
Self refers to whichever class you are currently in.

So if you’re typing code inside the Spaceship class, saying “self” refers to
the Spaceship class itself.

Examples of Self

#Code inside “Spaceship”


self.x = 50
self.y = 30
self.scaleX = 0.5
self.scaleY = 0.5

The code would make Spaceship move to the right by 50px, up by 30px,
and reduce its image size in half.

COLLISIONS
What Are Collisions?
Think of collision as checking whenever two objects touch. In Mario,
whenever he collides with a coin, it runs the code to add a score.

In PixelPAD, it’s when the “bounding boxes” of sprites touch. This includes
the transparent areas of the sprite as well!

We will use collisions in our game to determine when our ship is hit by
obstacles, when we’ve collected a power-up or health refill, and when we’ve
managed to shoot down an asteroid.

The get_collision Function


When we want to check for a collision between two objects, we use an if
statement combined with a special function called get_collision.

Here is an example of a get_collision function:

ű We start with an ordinary if statement.


ű For our condition, we specify get_collision.
ű We then write a pair of parentheses (()).
ű Next, we write self. This specifies that we want to check for collisions
against the current object.
ű Finally, we write “Asteroid”. This specifies the other class we want to
check for collisions with. In this case, we are checking for collisions with
any object created from the Asteroid class.
73
BOUNDING BOXES
Sprite’s Bounds
Every object has a bounding box, which is the rectangle that contains
the object’s entire sprite. The get_collision condition checks for overlaps
between the bounding boxes of objects, not the actual sprites. This can
sometimes create surprising results. Here is an example of two objects that
don’t look like they should be colliding, but do:

And here they are again, with their bounding boxes shown:

DESTROYING OBJECTS
The destroy Function
When two objects collide, we generally would like to destroy at least one
of them. Destroying an object removes it from the game. When an object
is destroyed, it no longer exists, and trying to use it could make your game
behave strangely or crash.

Destroying an object is very simple. Here is an example of destroying the


GAME GUIDE I GLOSSARY

player object:

74
THE START AND THE LOOP TABS
Start and Loop
Say you decide to go for a run. You put on your runners and then you run.
Running would be the loop because it repeats (one foot in front of the
other), and putting runners on would be the start because it only happens
in the beginning.

Similarly, in PixelPAD the “start” describes an instruction that only happens


once, such as the starting position of a robot. Whereas the “loop” could
describe its animation.

The Game Loop


Loops exist in our day-to-day life. For example, you wake up, get ready,
go to school, come back home, go to sleep and repeat these things every
day! So looping is the act of repeating. In programming, loops describe
instructions that repeat instead of having to code each instruction again
and again!.

Loops can happen every day, or they can repeat a specific number of times.
For example, a programmer can code a robot to jump 100 times, or code
the robot to keep jumping forever!

Video games are built around a game loop. Specifically for PixelPAD, our
game loop runs the code 60 times every second!

The loop starts when we click the Play button, and stops when we click the
Stop button. It goes around and around for as long as the game is playing,
updating each of our objects a little bit at a time.

75
How Do We Use The Game Loop?
When we write code for our objects, we can choose to place it in one of
two sections: the Start Section or the Loop Section. Code placed in the Start
Section is executed as soon as we create the object. Code placed in the
Loop Section, however, is added to the game loop, which means it will be
executed over and over until the game stops.

CONDITIONALS
Conditions
So far, whenever we’ve written any code, all we’ve done is give the
computer a list of commands to do one after the other. Using conditions,
we can tell the computer to make a decision between doing one thing or
another.

If Statements
The way we write conditions in our code is by using if statements. Here is an
example of a simple if statement:

ű Start with the word if


ű Next, we write our condition. The condition of an if statement is a true
or false question that we ask the computer to answer for us. In the above
example, our condition is key_is_pressed(‘D’), which is asking, “Is the D
key being pressed?”
ű After the condition, we write a full colon (:), and then make a new line.
ű Next, we indent our code, which means we start typing it a little bit further
to the right than we normally would
ű Finally, we write the body of the if statement. If the condition of the if
statement turns out to be true, then the computer will run whatever code
we put inside the body. In the above example, the body is
self.x = self.x + 1
INDENTATION IN PYTHON
Indentation
Indentation is when code is shifted to the right by adding at least two
GAME GUIDE I GLOSSARY

spaces to the left of the code. Indentation is important for two reasons:

ű Code that is indented is considered to be part of the body of the if


statement by the computer. As soon as we stop indenting the code, we
are no longer inside of the if statement.
ű Indentation helps us visually see the structure of our program based on
the shape of our code. This helps us navigate our code and find bugs
more easily.

76
For example:

We can see clearly that the statements only run if the condition is met. E.g.
the player presses the SPACE button.

It is very important that all of the code in the same body be indented using
the same number of spaces on every line.

KEY PRESS
Keyboard Input
One kind of condition we can use is a keyboard check. Keyboard checks
can be used to determine whether a keyboard key is being pressed or
not. We can use keyboard checks to make things happen when the player
presses or releases a keyboard key.

Keyboard checks are done using the key_is_pressed function. Here is an


example of using the key_is_pressed function:

if key_is_pressed(“W”):
print(“You are pressing the W key!”)

77
The code inside the apostrophes is the name of a keyboard key. Most keys
are named the same as the letter or word on their keyboard key. A few keys
have specific names:

ű The space bar’s name is space.


ű The arrow keys are named arrowLeft, arrowRight, arrowUp, and
arrowDown.
ű If you have a return key, it is named enter.

COMPARISONS
Comparisons
If we have code like: x > 300, this is a specific kind of condition called
a comparison. Comparisons are true/false questions we can ask the
computer about pairs of numbers. There are six main kinds of comparisons,
each with its own operator (special symbol). This table shows an example of
each kind of comparison:

Example Question Example Code


Is x smaller than y? x < y
Is x bigger than y? x > y
Is x smaller than or equal to y? x <= y
Is x bigger than or equal to y? x >= y
Is x equal to y? x == y
Is x not equal to y? x != y

There are other kinds of conditions, but comparisons are the kind that we
will be using most often.

Adding Boundaries Example


We can add boundaries to our game using if statements and comparisons.
GAME GUIDE I GLOSSARY

78
In our Player class’ Loop Section, add this new code at the bottom:

The Left Boundary

if self.x < -600:


self.x = -600

The Right Boundary

if self.x > 600:


self. x = 600

The Top Boundary

if self.y > 300:


self.y = 300

The Bottom Boundary

if self.y < -300:


self.y = -300

COMMENTS
Explaining Code with Comments
Computer code is complicated. That’s why, a long time ago, some very
smart programmers invented code comments.

Comments are like little notes that you can leave for yourself in your
programs. The computer completely ignores comments in your code. You
can write whatever you want inside of a comment.

How to Write a Comment


You can write a comment by starting a line of code with a pound sign,
which is the # symbol (you might call this symbol a hashtag). Here is an
example of some well-commented code:

Comments are an extremely useful tool, and you should get in the habit
of writing them. Comments help us remember what our code does, help
others understand our code, and help us keep our code organized.
79
TYPES OF BAD COMMENTS
Misleading Comments
It’s important to remember that comments are notes. The computer doesn’t
read our comments when it’s deciding what to do next. Because of this,
comments can sometimes be inaccurate. We should always read the code,
even if it is commented, to make sure it does what we think it is doing.

Here is an example of a misleading comment:

The comment says that this code makes the player move upwards, but
when we read the code, it actually makes them move downwards. If we just
read the comment without checking it against the code, we would have no
idea why our game wasn’t working properly.

Obvious Comments
Another type of bad comment is an obvious comment. Obvious comments
don’t add any meaningful information to your code; they usually just re-
state what the code is saying in plain English. Here is an example of an
obvious comment:

Obvious comments clutter up our code and can slowly turn into misleading
comments if we’re not careful. If a comment doesn’t add anything
meaningful to our code, it’s best to just delete it.

Vague Comments
Vague comments are comments that don’t actually explain anything. Vague
comments are usually written without much thought, or because the author
of the comment was told to comment on their code. Here is an example of
a vague comment:
GAME GUIDE I GLOSSARY

80
The comment at the top of this code just says “Grab it.” It doesn’t say what
the code does, or how it works. Some of this code doesn’t even have
anything obvious to do with grabbing. Similar to obvious comments, vague
comments clutter up our code and can slowly become misleading as we
work on our project. It is better to just delete any vague comments you find
in your code.

FRAMES PER SECOND


Frames
When you watch a movie, it looks like you’re seeing one single, moving
picture on the screen. This is a trick: a movie is a long series of slightly
different pictures, and those pictures are being shown to you so fast that
you can’t tell they’re individual images. Each of those single pictures is
called a frame.

Games use frames, too. Every time the code in an object’s Loop Section
runs, the game is drawing a new frame based on where our objects are and
what sprites we have attached to those objects.

Every frame in our game lasts exactly the same amount of time: 1/60th of a
second. That means that there are 60 frames in a second.

Timers
A timer is a number that counts time. For example, if we were watching a
clock, and counted up by one every time the clock’s second hand moved,
we would be timing seconds.

Since each frame in our games lasts the same amount of time, we can build
a timer that counts frames by counting up by one whenever our game’s
Loop Section is run.

Why are timers useful? Timers let us schedule things. For example, if we
wanted an asteroid to appear at the top of the screen every second, we
could use a timer that counted to 60 (since each frame lasts 1/60th of a
second).

81
RANDOM & IMPORT
Random Numbers
Most video games use some kind of randomness to change what happens
in the game each time we play, to stop the game from getting boring. We
can add randomness to our games using random numbers.

Random Positions
For example, whenever we create an asteroid, we’ve been using code like
this:

asteroid = Asteroid()
asteroid.x = 0
asteroid.y = 300

This code makes asteroids appear at the top of our screen, right in the
middle. When we play the game, every asteroid will appear in exactly the
same place. We can change this by asking for random numbers when we
set the asteroid’s position:

asteroid = Asteroid()
asteroid.x = random.randint(-600, 600)
asteroid.y = 300

Random Function
random.randint is a special command which asks for a random number. The
numbers between the parentheses are the smallest and largest values you
want to get. For example, if we were writing a dice-rolling game, we could
use random.randint(1, 6) to perform a dice roll.

Probability
Random numbers can be used to affect the probability that something will
happen in your program.

For example, in the code below we’re only creating an asteroid only half the
time we used to by adding the random.randint(1,2) == 1 conditional.

if asteroid_frames >= asteroid_timer:


GAME GUIDE I GLOSSARY

asteroid_frames = 0
if random.randint(1, 2) == 1:
asteroid = Asteroid()

This code randomly chooses between the numbers 1 and 2. If it chooses 1,


it creates an asteroid. If it chooses 2, it does not create an asteroid. Because
the random number will be 1 half of the time, and 2 the other half of the
time, this code will end up creating an asteroid half of the time as well.

82
Modules
When we want to use random numbers, we have to write another special
command at the very beginning of our program. This is the command:

import random

This is called importing a module. Since we don’t always need to use


random numbers, the random.randint command is normally turned off.
Importing the random module turns the random.randint command on, so
we can use it.

ROOMS & PERSISTENCE


Rooms
Rooms are the big sections of our game. At the very beginning of this
course, we set up a room for our game to happen in. Now that we’ve
finished building most of our game, it’s time to add a few new rooms.

Recall that when we want to change rooms, we use the room_set command.
This command does two things:

1. It automatically destroys every object that was part of the previous room

2. It runs the Start Section of the new room, which should create all the
objects that are part of the new room

Because each room controls all of the objects that are part of that room,
each room can be used to create an independent section of our game.

Persistent Objects
Persistent objects do not belong to any room, and are never automatically
destroyed by the room_set command. Persistent objects can be useful,
but we have to be extremely careful to clean them up with the destroy
command when we don’t need them any more.

To turn an object persistent you can use the code self.persistent = True in
the Start tab of the class

83
ERRORS

TYPES OF ERRORS

Compile-time Errors
Sometimes, we make mistakes when we write code. We mean to type x, but
accidentally type y. We accidentally write If instead of if. These are called
programmer errors.

A compile-time error is an error that results from the programmer writing


code incorrectly. Another way of thinking about it is any error that produces
an error message.

Compile-time errors are generally easy to find and fix, because they tend to
produce detailed error messages with line numbers and file names.

Runtime Errors

On the other hand, sometimes we’ve written our code in the correct way,
but it doesn’t do what we expect it to do. For example, we could expect
an object to move in one direction, but it ends up moving in the opposite
direction. These are called runtime errors, and are much harder to debug.

Runtime errors occur when code is written without mistakes, but does not
behave correctly.

The easiest way to find and fix runtime errors is to use the debug loop.
Many problems are caused by incorrect assumptions, so make sure to
always reread your code thoroughly to make sure it is doing what you think
it is doing.

DEBUGGING
Debugging
Debugging is when we find and fix problems in our programs. Debugging
is very important, because it’s very easy to make mistakes when we write
code. Even the very best programmers need to debug their code every
day.

The Debug Loop


When we’re fixing our programs, we can always just change code at
GAME GUIDE I GLOSSARY

random until our program behaves the way we want it to. If we’re persistent,
we can fix problems this way, but it’s not a very fast (or easy!) way to work.

84
A better way to debug is to use the debug loop. The debug loop is a simple
process that we repeat until our program works properly. This is what it
looks like:

1. First, we Run our code. Running our code will let us observe it,
which will show us whether there are any errors or other problems. If
everything is working properly, we can stop debugging.

2. Next, we Read our code. Using what we learned from running our
code, we look for specific commands that might be causing problems.
Sometimes, an error message will tell us exactly where to look by giving
us a line number and file name (for example, error in Player.Loop() on
line 4 means that the 4th line of code in the Player class’ loop tab is
wrong). When we don’t have an error message, we have to look for the
problem ourselves.

3. Lastly, we Change our code a tiny little bit. Once we think we’ve found
the source of a bug, we can change our code to either make it give us
more information (this is called tracing), or we can try to fix the problem.
It is important to change only a small amount of code in this step,
because whenever we change our code, we risk adding new bugs to
our program.

LOGGING IN
Logging onto PixelPAD
We will access PixelPAD using an internet browser such as Google Chrome,
Firefox, or Safari. This way you can play and create your game from any
computer! Go onto https://www.pixelpad.io

1. Click Login / Sign Up

2. Your username and password will be provided for you! If you don’t have
a username, please speak to one of your facilitators!

3. Click on Learn, and select the Game Tutorial you’d like to work on. This
will create a blank project of a game with the tutorial open to get you
started.

85
CHALLENGE
QUESTIONS

CHAPTER 1

What is the difference between the Learn and MyPAD section on PixelPAD?

What are some examples of objects or services that heavily incorporate


UI/ UX design?

CHAPTER 2
GAME GUIDE I CHALLENGE QUESTIONS

If our game is running at 60 Frames per Second, how many frames is:

a) One Minute?

b) One Hour?

Bonus: One Year?

86
CHAPTER 3

For each of the following pieces of code, in what direction is the object
moving in?

self.y = self.y + 3

a) Up

b) Down

c) Right

self.x = self.x - 7

a) Up

b) Right

c) Left

self.x = self.x - 3
self.y = self.y + 4

a) Up and Right

b) Up and Left

c) Down and Left

CHAPTER 4

Where do you assign an object a team?

a) In an Class’ Loop Tab

b) In an Class’ Start Tab

c) On a Sprite

87
CHAPTER 5

If you create a variable in the game script, where can you access it from?

a) The game script only

b) Any script, by declaring a new variable

c) Any script, by referring to game.variableName

CHAPTER 6

If you had to add one more feature to your game, what would it be?

How would you code it? (Explain in words)


GAME GUIDE I CHALLENGE QUESTIONS

88
89

You might also like