context.fillStyle will set the color for all future fills such as
context.fillRect(). It's important to note theat context.fillStyle
is not a method, it is an attribute. You set it like so:
context.lineTo(x, y) creates a path from the current point - default (0,0) -
to the point (x, y). In order to see the line you must call context.stroke().
context.save() saves all of the properties of the context. Then, when you call
context.restore() it will reset all changes to the context you've made.
This code will produce the image below (note how the second rectangle is black
and closer to the original origin despite calling translate and setting the
fillStyle earlier):
context.scale(percentX, percentY) scales the x and y coordinates of the context, making drawings
larger or smaller.
This code will produce the image below (note that all rectangles are defined to be the same size
in the code are drawn in different sizes due to the scale of the context):
context.translate(x, y) shifts the origin of the context (where the (0,0) point is)
right by x and down by y. This allows you to draw execute multiple commands in a specific
area of the context more easily.
This code will produce the image below (note how the fillRect commands are
relative to the translation point (100,100)):
Math.round(), Math.floor() and Math.ceil() will round a number to an integer.
Math.floor() will always round down while Math.ceil() will always round up.
Math.round() will round to the closest integer, either up or down.
Math.random() will produce a random decimal betweek 0 and 1. Calling it many
times will produce a different random number each time.
Often you will multiple Math.random() by another number to get a random
integer larger than 1. You may also call Math.floor() or Math.ceil() on the result
to get an rounded integer.
We need to make sure that if our flappy square runs into them
then we end the game.
It would also be good to randomize how often they appear and how
big the hole is that the flappy square needs to fly through.
Lesson: Collision Detection
Collision detection is very common in games. You don't want your
characters running through walls like our flappy square currently is.
In order to detect the collision with something else in the game you
need to compare the position and size of everything in the game to make
sure nothing overlaps. Unfortunately it takes quite a few calculations to
check every object (and you have to check each side of the object)
against every other object. Luckily the computer is very good at making these
calculations so we can easily make these calculation each frame without slowing
the animation down.
In this example we'll show a simple collision against a wall. Since we
are only moving right we only need to check to see if the right side of the
square overlaps with the left edge of the wall at all.
This requires a simple check:
if (box.x + box.size >= wallPosition) {
... end the animation ...
}
This is checking to see if the right side of the box (box.x + box.size)
has move passed the left side of the wall (its "x" coordinate). If
it has then we know a collision has taking place.
var canvas = document.getElementById('flappy_square_stage4_lesson1');
var context = canvas.getContext('2d');
var interval;
var time = 0;
var box = {
size: 20,
x: 50,
y: 100,
speed: 3
};
var wallPosition = 300;
function moveBox() {
box.x += box.speed;
}
function drawBox() {
context.fillRect(box.x, box.y, box.size, box.size);
}
function drawWall() {
context.fillRect(
wallPosition,
0,
canvas.width - wallPosition,
canvas.height
);
}
function checkWallCollision() {
if (box.x + box.size >= wallPosition) {
clearInterval(interval);
context.fillStyle = 'red';
drawBox();
drawWall();
context.font = "30px serif";
context.textAlign = 'left';
context.textBaseline = 'middle';
context.fillText('BANG!', 100, canvas.height / 2);
}
}
function programSteps() {
context.clearRect(0, 0, canvas.width, canvas.height);
moveBox();
drawBox();
drawWall();
checkWallCollision();
}
function runProgram() {
interval = setInterval(programSteps, 50);
}
// The following code is provided for you.
// It creates an eventListener that listens
// for the canvas to come into "focus", which
// happens when you click on it.
// This allows us to stop and start each individual
// animation on this whole page separately.
function startAnimation() {
runProgram();
}
function pauseAnimation() {
clearInterval(interval);
context.font = "20px serif";
context.textAlign = 'center';
context.fillText('Click anywhere on the canvas to start the animation.', canvas.width / 2, 50);
}
canvas.addEventListener('focus', startAnimation);
canvas.addEventListener('blur', pauseAnimation);
pauseAnimation();
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
Challenge 1
So now we're going to take our code from Stage
3
and we're going
to track whether our flappy square runs into a wall.
Collision detection is one of the more complicated problems you'll
deal with when developing a game. The lesson above demonstrates how
complex the calculations are. So take your time with this challenge
and don't get discouraged. It's hard stuff.
Luckily because our flappy square is flying constantly to the right
we don't need to worry about collisions to the left. This simplifies
things a bit. When the flappy square is in the same "x" coordinates
as a wall we need to check to see if the flappy square is passing
through the hole in the wall.
In each frame we'll find the wall that the flappy square is flying
through (if any) and make sure it is not flying through any of the
"wall" area.
So, for example, if we wanted to check whether flappy square had passed
beyond the right side of a wall (really the right side of the wall has
passed the stationary flappy square) we could do something like this:
var left = square.x;
var wallRight = wallX + wall.width;
if (left > wallRight) {
... right side of wall has passed the left side of the flappy square ...
}
We'll also need to keep track of the positions of each wall to find
the wall the flappy square is flying through.
You can do this by recording the "x" coordinate of each wall in a variable:
var wall = {
...
positions: []
}
...
wall.positions.push(wallX);
Then, you can iterate through these positions like so:
for (var i=0; i < wall.positions.length; ++i) {
var position = wall.positions[i];
}
You just need to be careful not to store the same wall multiple
times. You may want to store values first, only storing the positions
of new walls, and then draw all of the walls.
This is a
for loop
and you can use it to perform an action using each of the positions in
your
position
array.
In the example code below we find the wall that the flappy square is passing
through and check to see that the flappy square is above the bottom
of the top part of the wall (not going through the hole). If it is
then we call the method, endGame(), that we currently call from
checkBoundary() to end the game.
var left = square.x;
var right = square.x + square.size;
var top = square.y;
var bottom = square.y + square.size;
for (var i=0; i < wall.positions.length; ++i) {
var wallLeft = wall.positions[i] - distance;
var wallRight = wallLeft + wall.width;
if (wallLeft > right) continue;
if (wallRight < left) continue;
var topWallBottom = wall.height;
if (top < topWallBottom) {
endGame();
}
}
Remember this is only checking if the flappy square is hitting the
top portion of the wall. You'll need to ensure it isn't hitting the bottom
portion as well.
It's a bit complicated. Try to imagine each interaction (e.g. the square
moving to the right and running into a top wall) and figuring out how you would
know that the right side of the square has connected with the left side of
the wall while the square is above the bottom of the wall (for the top part
of the wall).
Yea, it's complicated.
Take it slowly and try to work it out.
In the end your flappy square should behave like the example provided at
the beginning of this description.
Previous Challenge:
View your
code from
Stage
3
Challenge
5
to use on this challenge.
Code Missing:
You have not yet entered any code in to the previous challenge:
Stage
3
Challenge
5
Stage
3
Challenge
5
Editor
(write code below)
var canvas = document.getElementById('flappy_square_stage4_challenge1');
var context = canvas.getContext('2d');
var interval;
var distance = 0;
var gravity = 0.5;
var boundary = {
minX: 25,
minY: 25,
width: 425,
height: 275
};
var square = {
x: 25,
y: 75,
size: 20,
yVelocity: 0,
jump: -8
};
var wall = {
spacing:
width:
height:
positions: []
};
function drawBoundary() {
}
function drawSquare() {
}
function drawWall(x) {
}
function drawWalls() {
// Remember to track their positions
// in wall.positions as well.
}
function flap() {
}
function adjustPosition() {
}
function clearBoundary() {
}
function checkBoundary() {
}
function checkWalls() {
// CODE TO CHECK FOR WALL COLLISIONS HERE
}
function endGame() {
context.font = "20px serif";
context.textAlign = 'center';
var xCenter = boundary.width / 2;
var yCenter = boundary.height / 2;
context.fillText('Game Over', xCenter, yCenter);
pauseAnimation();
}
function programSteps() {
// Remember to run checkBoundary()
// and checkWalls() from there.
}
function runProgram() {
interval =
}
canvas.addEventListener('click', flap);
// The following code is provided for you.
// It creates an eventListener that listens
// for the canvas to come into "focus", which
// happens when you click on it.
// This allows us to stop and start each individual
// animation on this whole page separately.
function startAnimation() {
runProgram();
}
function pauseAnimation() {
clearInterval(interval);
}
canvas.addEventListener('focus', startAnimation);
canvas.addEventListener('blur', pauseAnimation);
canvas.focus();
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
A Solution:
Here's the code I wrote to complete this challenge.
View One Possible Solution
var canvas = document.getElementById('flappy_square_stage4_challenge1');
var context = canvas.getContext('2d');
var interval;
var distance = 0;
var gravity = 0.5;
var boundary = {
minX: 25,
minY: 25,
width: 425,
height: 275
};
var square = {
x: 25,
y: 75,
size: 20,
yVelocity: 0,
jump: -8
};
var wall = {
spacing: 125,
width: 50,
height: 100,
positions: []
};
function drawBoundary() {
context.strokeRect(0, 0, boundary.width, boundary.height);
}
function drawSquare() {
context.fillRect(square.x, square.y, square.size, square.size);
}
function drawWall(x) {
context.fillRect(x, 0, wall.width, wall.height);
context.fillRect(x, boundary.height - wall.height, wall.width, wall.height);
}
function drawWalls() {
var wallX = distance - wall.width;
if (wall.positions.length > 0) {
wallX = wall.positions[wall.positions.length - 1];
} else if (wallX < wall.spacing) {
wallX = wall.spacing;
}
while (wallX < canvas.width + distance + wall.width) {
if (wallX % wall.spacing === 0) {
wall.positions.push(wallX);
}
wallX += 1;
}
for (var i=0; i < wall.positions.length; ++i) {
var position = wall.positions[i];
drawWall(position - distance);
}
}
function flap() {
square.yVelocity = square.jump;
}
function adjustPosition() {
distance += 2;
square.yVelocity += gravity;
square.y += square.yVelocity;
}
function clearBoundary() {
var maxX = boundary.minX + boundary.width;
var maxY = boundary.minY + boundary.height;
context.clearRect(0, 0, canvas.width, boundary.minY);
context.clearRect(maxX, 0, canvas.width - maxX, canvas.height);
context.clearRect(0, maxY, canvas.width, canvas.height - maxY);
context.clearRect(0, 0, boundary.minX, canvas.height);
}
function checkBoundary() {
if (square.y >= boundary.maxY) {
endGame();
}
}
function checkWalls() {
var left = square.x;
var right = square.x + square.size;
var top = square.y;
var bottom = square.y + square.size;
for (var i=0; i < wall.positions.length; ++i) {
var wallLeft = wall.positions[i] - distance;
var wallRight = wallLeft + wall.width;
if (wallLeft > right) continue;
if (wallRight < left) continue;
var topWallBottom = boundary.minY + wall.height;
var bottomWallTop = boundary.maxY - wall.height;
if (top < topWallBottom || bottom > bottomWallTop) {
endGame();
}
}
}
function endGame() {
context.font = "20px serif";
context.textAlign = 'center';
var xCenter = boundary.width / 2;
var yCenter = boundary.height / 2;
context.fillText('Game Over', xCenter, yCenter);
pauseAnimation();
}
function programSteps() {
context.clearRect(0, 0, canvas.width, canvas.height);
adjustPosition();
context.save();
context.translate(boundary.minX, boundary.minY);
drawBoundary();
drawSquare();
drawWalls();
checkWalls();
checkBoundary();
context.restore();
clearBoundary();
}
function runProgram() {
interval = setInterval(programSteps, 80);
}
canvas.addEventListener('click', flap);
// The following code is provided for you.
// It creates an eventListener that listens
// for the canvas to come into "focus", which
// happens when you click on it.
// This allows us to stop and start each individual
// animation on this whole page separately.
function startAnimation() {
runProgram();
}
function pauseAnimation() {
clearInterval(interval);
}
canvas.addEventListener('focus', startAnimation);
canvas.addEventListener('blur', pauseAnimation);
canvas.focus();
Lesson: Randomization
Our game isn't very interesting with every wall looking the exact same.
Randomizing the spacing between the walls, and the location and size of
the hole in the wall would make the game more interesting and challenging.
In this lesson we look at creating a drawing using random numbers.
We'll create a simple cityscape using rectangles that are different widths
and heights drawn next to each other.
The
Math.random()
method allows us to randomize this process so if you hit "Run" multiple
times you'll see the randomly generated "buildings" change each time.
So if we wanted to generate a random number between 40 and 200 for our
building height we can do something like this:
40 + Math.floor(Math.random() * 160)
The
Math.random()
generates a random decimal between 0 and 1 (such as 0.8). We multiply
that decimal by the maximum number we want (in this case 160 or 200 - 40)
to get a number between 0 and that maximum number.
Math.floor()
then rounds that number down (for example from 129.83 to just 129) and we
add 40 to give us a random integer between 40 and 200.
var canvas = document.getElementById('flappy_square_stage4_lesson2');
var context = canvas.getContext('2d');
var ground = canvas.height - 20;
function drawBuilding(x, width, height) {
context.fillRect(x, ground - height, width, height);
}
function random(lowerBound, upperBound) {
return lowerBound + Math.floor(
Math.random() * (upperBound - lowerBound)
);
}
var totalX = 0;
while(totalX < canvas.width) {
var width = random(40, 100);
var height = random(50, 250);
drawBuilding(totalX, width, height);
totalX += width + 2;
}
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
Challenge 2
Our game landscape is a little repetitive. Each wall is equally spaced apart
and the holes through the walls are all the same size and in the same position.
We need to mix it up a bit more.
Let's take our code from challenge 1 and randomize both the spacing of the walls
and the size and position of the holes.
Use the
Math.random()
method to produce a random decimal between 0 and 1.
Then multiply that decimal by a larger number to create a random number
between 0 and that number. Use
Math.floor()
to produce an integer. Then you can add to that randomly produced integer
to change the range (for example add 40 to go from 0 - 160 to 40 - 200).
This looks something like this:
40 + Math.floor(Math.random() * 160)
If you run this multiple times you'll get values ranging between 40 and 200.
Use something similar to
position walls between 100 and 200 pixels apart.
You also need to keep track of where you position the walls so that they don't
move around randomly as you draw each frame. Make sure that your wall collision
detection works properly with their new random positions.
This is harder than it sounds when you're dealing with a moving background.
You'll need to position enough walls so that as the background moves the walls
keep coming, indefinitely.
There are many ways to do this, but I'd suggest you position walls until at least
one wall is outside the bounds of the canvas. Once that wall is within the bounds
of the canvas then you position the next wall off the canvas. If you continue doing
this then you'll always have one wall outside of the bounds of the game ready to
come in to view.
Let's also widen the hole in each wall so that we can easily fly through them
and observe the random spacing between our walls more easily. Make
the
height of the walls 75 instead of 100.
In the end your flappy square should behave like the example provided at
the beginning of this description.
Previous Challenge:
View your
code from
Stage
4
Challenge
1
to use on this challenge.
Code Missing:
You have not yet entered any code in to the previous challenge:
Stage
4
Challenge
1
Stage
4
Challenge
1
Editor
(write code below)
var canvas = document.getElementById('flappy_square_stage4_challenge2');
var context = canvas.getContext('2d');
var interval;
var distance = 0;
var gravity = 0.5;
var boundary = {
minX: 25,
minY: 25,
width: 425,
height: 275
};
var square = {
x: 25,
y: 75,
size: 20,
yVelocity: 0,
jump: -8
};
var wall = {
width:
height:
positions: []
};
function drawBoundary() {
}
function drawSquare() {
}
function drawWall(x) {
}
function drawWalls() {
// Use Math.random() to position
// the walls randomly. Remember to
// track the position of each wall.
}
function flap() {
}
function adjustPosition() {
}
function clearBoundary() {
}
function checkBoundary() {
}
function checkWalls() {
}
function endGame() {
context.font = "20px serif";
context.textAlign = 'center';
var xCenter = boundary.width / 2;
var yCenter = boundary.height / 2;
context.fillText('Game Over', xCenter, yCenter);
pauseAnimation();
}
function programSteps() {
}
function runProgram() {
interval =
}
canvas.addEventListener('click', flap);
// The following code is provided for you.
// It creates an eventListener that listens
// for the canvas to come into "focus", which
// happens when you click on it.
// This allows us to stop and start each individual
// animation on this whole page separately.
function startAnimation() {
runProgram();
}
function pauseAnimation() {
clearInterval(interval);
}
canvas.addEventListener('focus', startAnimation);
canvas.addEventListener('blur', pauseAnimation);
canvas.focus();
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
A Solution:
Here's the code I wrote to complete this challenge.
View One Possible Solution
var canvas = document.getElementById('flappy_square_stage4_challenge2');
var context = canvas.getContext('2d');
var interval;
var distance = 0;
var gravity = 0.5;
var boundary = {
minX: 25,
minY: 25,
width: 425,
height: 275
};
var square = {
x: 25,
y: 75,
size: 20,
yVelocity: 0,
jump: -8
};
var wall = {
width: 50,
height: 75,
positions: []
};
function drawBoundary() {
context.strokeRect(0, 0, boundary.width, boundary.height);
}
function drawSquare() {
context.fillRect(square.x, square.y, square.size, square.size);
}
function drawWall(x) {
context.fillRect(x, 0, wall.width, wall.height);
context.fillRect(x, boundary.height - wall.height, wall.width, wall.height);
}
function drawWalls() {
var wallX;
if (wall.positions.length > 0) {
wallX = wall.positions[wall.positions.length - 1];
} else {
wallX = boundary.minX + 125;
wall.positions.push(wallX);
}
while (wallX < canvas.width + distance + wall.width) {
wallX += (100 + Math.floor(Math.random() * 100));
wall.positions.push(wallX);
}
for (var i=0; i < wall.positions.length; ++i) {
var position = wall.positions[i];
drawWall(position - distance);
}
}
function flap() {
square.yVelocity = square.jump;
}
function adjustPosition() {
distance += 2;
square.yVelocity += gravity;
square.y += square.yVelocity;
}
function clearBoundary() {
var maxX = boundary.minX + boundary.width;
var maxY = boundary.minY + boundary.height;
context.clearRect(0, 0, canvas.width, boundary.minY);
context.clearRect(maxX, 0, canvas.width - maxX, canvas.height);
context.clearRect(0, maxY, canvas.width, canvas.height - maxY);
context.clearRect(0, 0, boundary.minX, canvas.height);
}
function checkBoundary() {
if (square.y >= boundary.height) {
endGame();
}
}
function checkWalls() {
var left = square.x;
var right = square.x + square.size;
var top = square.y;
var bottom = square.y + square.size;
for (var i=0; i < wall.positions.length; ++i) {
var wallLeft = wall.positions[i] - distance;
var wallRight = wallLeft + wall.width;
if (wallLeft > right) continue;
if (wallRight < left) continue;
var topWallBottom = wall.height;
var bottomWallTop = boundary.height - wall.height;
if (top < topWallBottom || bottom > bottomWallTop) {
endGame();
}
}
}
function endGame() {
context.font = "20px serif";
context.textAlign = 'center';
var xCenter = boundary.width / 2;
var yCenter = boundary.height / 2;
context.fillText('Game Over', xCenter, yCenter);
pauseAnimation();
}
function programSteps() {
context.clearRect(0, 0, canvas.width, canvas.height);
adjustPosition();
context.save();
context.translate(boundary.minX, boundary.minY);
drawBoundary();
drawSquare();
drawWalls();
checkWalls();
checkBoundary();
context.restore();
clearBoundary();
}
function runProgram() {
interval = setInterval(programSteps, 80);
}
canvas.addEventListener('click', flap);
// The following code is provided for you.
// It creates an eventListener that listens
// for the canvas to come into "focus", which
// happens when you click on it.
// This allows us to stop and start each individual
// animation on this whole page separately.
function startAnimation() {
runProgram();
}
function pauseAnimation() {
clearInterval(interval);
}
canvas.addEventListener('focus', startAnimation);
canvas.addEventListener('blur', pauseAnimation);
canvas.focus();
Challenge 3
Ok, now that we have a better understanding of randomization let's apply it to the
position and size of the hole in each wall.
Rather than having each portion of the wall be the same height (75 pixels) let's
vary them by randomizing the height of the top and bottom portion of the wall,
creating varying hole positions and sizes.
You can play around with varying sizes, but I'd suggest varying the top portion
between 25 and 150 pixels.
Then you'll want to create a varying height for the bottom portion that
is within the same parameters but you need to be careful. If you
randomly create two walls that are 150 pixels then you won't have any room
for the hole and you probably want the hole to be at least
75 pixels.
Similarly if you both the top and bottom portions of the wall are 25 pixels then
your hole will be 225 pixels (300 - 25 - 50), which is very big. So you may
want to make your hole no more than 150 pixels as well...
You'll need to store this information as well so that you can draw the same hole
in each frame. You may want to store a hash in the "positions" attribute of your "walls"
variable that contains the position, the height of the top wall, and the size of the
hole.
You'll need to update your wall collision detection to take this information into
account as well.
In the end your flappy square should behave like the example to the right.
Previous Challenge:
View your
code from
Stage
4
Challenge
2
to use on this challenge.
Code Missing:
You have not yet entered any code in to the previous challenge:
Stage
4
Challenge
2
Stage
4
Challenge
2
Editor
(write code below)
var canvas = document.getElementById('flappy_square_stage4_challenge3');
var context = canvas.getContext('2d');
var interval;
var distance = 0;
var gravity = 0.5;
var boundary = {
minX: 25,
minY: 25,
width: 425,
height: 275
};
var square = {
x: 25,
y: 75,
size: 20,
yVelocity: 0,
jump: -8
};
var wall = {
width:
height:
positions: []
};
function drawBoundary() {
}
function drawSquare() {
}
function drawWall(wallInfo) {
// You'll need to pass in all of the
// wall information and use it to
// draw a wall with the specified
// top and bottom heights.
// You may also want to account for
// distance here now as well.
}
function drawWalls() {
// Set the height of the top and
// bottom portions of the wall
// randomly and track them.
}
function flap() {
}
function adjustPosition() {
}
function clearBoundary() {
}
function checkBoundary() {
}
function checkWalls() {
// Be sure to incorporate
// the new randomly generated
// heights.
}
function endGame() {
context.font = "20px serif";
context.textAlign = 'center';
var xCenter = boundary.width / 2;
var yCenter = boundary.height / 2;
context.fillText('Game Over', xCenter, yCenter);
pauseAnimation();
}
function programSteps() {
}
function runProgram() {
interval =
}
canvas.addEventListener('click', flap);
// The following code is provided for you.
// It creates an eventListener that listens
// for the canvas to come into "focus", which
// happens when you click on it.
// This allows us to stop and start each individual
// animation on this whole page separately.
function startAnimation() {
runProgram();
}
function pauseAnimation() {
clearInterval(interval);
}
canvas.addEventListener('focus', startAnimation);
canvas.addEventListener('blur', pauseAnimation);
canvas.focus();
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
A Solution:
Here's the code I wrote to complete this challenge.
View One Possible Solution
var canvas = document.getElementById('flappy_square_stage4_challenge3');
var context = canvas.getContext('2d');
var interval;
var distance = 0;
var gravity = 0.5;
var boundary = {
minX: 25,
minY: 25,
width: 425,
height: 275
};
var square = {
x: 25,
y: 75,
size: 20,
yVelocity: 0,
jump: -8
};
var wall = {
width: 50,
minHole: 75,
maxHole: 150,
positions: []
};
function drawBoundary() {
context.strokeRect(0, 0, boundary.width, boundary.height);
}
function drawSquare() {
context.fillRect(square.x, square.y, square.size, square.size);
}
function drawWall(wallInfo) {
var x = wallInfo.x - distance;
context.fillRect(
x,
0,
wall.width,
wallInfo.topHeight
);
context.fillRect(
x,
boundary.height - wallInfo.bottomHeight,
wall.width,
wallInfo.bottomHeight
);
}
function random(min, max) {
return (min + Math.floor(Math.random() * (max - min)));
}
function drawWalls() {
var wallX = square.x;
if (wall.positions.length > 0) {
wallX = wall.positions[wall.positions.length - 1].x;
}
var end = boundary.width + distance + wall.width;
while (wallX < end) {
wallX += random(100, 200);
var topHeight = random(25, 150);
var bottomHeight = random(25, 150);
if (boundary.height - topHeight - bottomHeight < wall.minHole) {
bottomHeight = boundary.height - topHeight - wall.minHole;
}
if (boundary.height - topHeight - bottomHeight > wall.maxHole) {
bottomHeight = boundary.height - topHeight - wall.maxHole;
}
wall.positions.push({
x: wallX,
topHeight: topHeight,
bottomHeight: bottomHeight
});
}
for (var i=0; i < wall.positions.length; ++i) {
drawWall(wall.positions[i]);
}
}
function flap() {
square.yVelocity = square.jump;
}
function adjustPosition() {
distance += 2;
square.yVelocity += gravity;
square.y += square.yVelocity;
}
function clearBoundary() {
var maxX = boundary.minX + boundary.width;
var maxY = boundary.minY + boundary.height;
context.clearRect(0, 0, canvas.width, boundary.minY);
context.clearRect(maxX, 0, canvas.width - maxX, canvas.height);
context.clearRect(0, maxY, canvas.width, canvas.height - maxY);
context.clearRect(0, 0, boundary.minX, canvas.height);
}
function checkBoundary() {
if (square.y >= boundary.height) {
endGame();
}
}
function checkWalls() {
var left = square.x;
var right = square.x + square.size;
var top = square.y;
var bottom = square.y + square.size;
for (var i=0; i < wall.positions.length; ++i) {
var wallInfo = wall.positions[i];
var wallLeft = wallInfo.x - distance;
var wallRight = wallLeft + wall.width;
if (wallLeft > right) continue;
if (wallRight < left) continue;
var topWallBottom = wallInfo.topHeight;
var bottomWallTop = boundary.height - wallInfo.bottomHeight;
if (top < topWallBottom || bottom > bottomWallTop) {
endGame();
}
}
}
function endGame() {
context.font = "20px serif";
context.textAlign = 'center';
var xCenter = boundary.width / 2;
var yCenter = boundary.height / 2;
context.fillText('Game Over', xCenter, yCenter);
pauseAnimation();
}
function programSteps() {
context.clearRect(0, 0, canvas.width, canvas.height);
adjustPosition();
context.save();
context.translate(boundary.minX, boundary.minY);
drawBoundary();
drawSquare();
drawWalls();
checkWalls();
checkBoundary();
context.restore();
clearBoundary();
}
function runProgram() {
interval = setInterval(programSteps, 80);
}
canvas.addEventListener('click', flap);
// The following code is provided for you.
// It creates an eventListener that listens
// for the canvas to come into "focus", which
// happens when you click on it.
// This allows us to stop and start each individual
// animation on this whole page separately.
function startAnimation() {
runProgram();
}
function pauseAnimation() {
clearInterval(interval);
}
canvas.addEventListener('focus', startAnimation);
canvas.addEventListener('blur', pauseAnimation);
canvas.focus();