curriculum/challenges/english/blocks/learn-intermediate-oop-by-building-a-platformer-game/650755908a8071409ab9e09e.md
The next step is to write a condition that checks if the player has reached the last checkpoint.
Start by adding an if statement that checks if the index is equal to the length of the checkpoints array minus one.
You should have an empty if statement with the condition index === checkpoints.length - 1
assert.match(code, /if\s*\(\s*index\s*===\s*checkpoints\.length\s*-\s*1\s*\)\s*\{\s*\}\s*;?/)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Learn Intermediate OOP by Building a Platformer Game</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<div class="start-screen">
<h1 class="main-title">freeCodeCamp Code Warrior</h1>
<p class="instructions">
Help the main player navigate to the yellow checkpoints.
</p>
<p class="instructions">
Use the keyboard arrows to move the player around.
</p>
<p class="instructions">You can also use the spacebar to jump.</p>
<div class="btn-container">
<button class="btn" id="start-btn">Start Game</button>
</div>
</div>
<div class="checkpoint-screen">
<h2>Congrats!</h2>
<p>You reached the last checkpoint.</p>
</div>
<canvas id="canvas"></canvas>
<script src="./script.js"></script>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #0a0a23;
--section-bg-color: #ffffff;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
}
.start-screen {
background-color: var(--section-bg-color);
width: 100%;
position: absolute;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
border-radius: 30px;
padding: 20px;
padding-bottom: 5px;
}
.main-title {
text-align: center;
}
.instructions {
text-align: center;
font-size: 1.2rem;
margin: 15px;
line-height: 2rem;
}
.btn {
cursor: pointer;
width: 100px;
margin: 10px;
color: #0a0a23;
font-size: 18px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
.btn:hover {
background-image: linear-gradient(#ffcc4c, #f89808);
}
.btn-container {
display: flex;
align-items: center;
justify-content: center;
}
.checkpoint-screen {
position: absolute;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
width: 100%;
text-align: center;
background-color: var(--section-bg-color);
border-radius: 20px;
padding: 10px;
display: none;
}
#canvas {
display: none;
}
@media (min-width: 768px) {
.start-screen {
width: 60%;
max-width: 700px;
}
.checkpoint-screen {
max-width: 300px;
}
}
const startBtn = document.getElementById("start-btn");
const canvas = document.getElementById("canvas");
const startScreen = document.querySelector(".start-screen");
const checkpointScreen = document.querySelector(".checkpoint-screen");
const checkpointMessage = document.querySelector(".checkpoint-screen > p");
const ctx = canvas.getContext("2d");
canvas.width = innerWidth;
canvas.height = innerHeight;
const gravity = 0.5;
let isCheckpointCollisionDetectionActive = true;
const proportionalSize = (size) => {
return innerHeight < 500 ? Math.ceil((size / 500) * innerHeight) : size;
}
class Player {
constructor() {
this.position = {
x: proportionalSize(10),
y: proportionalSize(400),
};
this.velocity = {
x: 0,
y: 0,
};
this.width = proportionalSize(40);
this.height = proportionalSize(40);
}
draw() {
ctx.fillStyle = "#99c9ff";
ctx.fillRect(this.position.x, this.position.y, this.width, this.height);
}
update() {
this.draw();
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
if (this.position.y + this.height + this.velocity.y <= canvas.height) {
if (this.position.y < 0) {
this.position.y = 0;
this.velocity.y = gravity;
}
this.velocity.y += gravity;
} else {
this.velocity.y = 0;
}
if (this.position.x < this.width) {
this.position.x = this.width;
}
if (this.position.x >= canvas.width - this.width * 2) {
this.position.x = canvas.width - this.width * 2;
}
}
}
class Platform {
constructor(x, y) {
this.position = {
x,
y,
};
this.width = 200;
this.height = proportionalSize(40);
}
draw() {
ctx.fillStyle = "#acd157";
ctx.fillRect(this.position.x, this.position.y, this.width, this.height);
}
}
class CheckPoint {
constructor(x, y, z) {
this.position = {
x,
y,
};
this.width = proportionalSize(40);
this.height = proportionalSize(70);
this.claimed = false;
};
draw() {
ctx.fillStyle = "#f1be32";
ctx.fillRect(this.position.x, this.position.y, this.width, this.height);
}
claim() {
this.width = 0;
this.height = 0;
this.position.y = Infinity;
this.claimed = true;
}
};
const player = new Player();
const platformPositions = [
{ x: 500, y: proportionalSize(450) },
{ x: 700, y: proportionalSize(400) },
{ x: 850, y: proportionalSize(350) },
{ x: 900, y: proportionalSize(350) },
{ x: 1050, y: proportionalSize(150) },
{ x: 2500, y: proportionalSize(450) },
{ x: 2900, y: proportionalSize(400) },
{ x: 3150, y: proportionalSize(350) },
{ x: 3900, y: proportionalSize(450) },
{ x: 4200, y: proportionalSize(400) },
{ x: 4400, y: proportionalSize(200) },
{ x: 4700, y: proportionalSize(150) },
];
const platforms = platformPositions.map(
(platform) => new Platform(platform.x, platform.y)
);
const checkpointPositions = [
{ x: 1170, y: proportionalSize(80), z: 1 },
{ x: 2900, y: proportionalSize(330), z: 2 },
{ x: 4800, y: proportionalSize(80), z: 3 },
];
const checkpoints = checkpointPositions.map(
(checkpoint) => new CheckPoint(checkpoint.x, checkpoint.y, checkpoint.z)
);
const animate = () => {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height);
platforms.forEach((platform) => {
platform.draw();
});
checkpoints.forEach(checkpoint => {
checkpoint.draw();
});
player.update();
if (keys.rightKey.pressed && player.position.x < proportionalSize(400)) {
player.velocity.x = 5;
} else if (keys.leftKey.pressed && player.position.x > proportionalSize(100)) {
player.velocity.x = -5;
} else {
player.velocity.x = 0;
if (keys.rightKey.pressed && isCheckpointCollisionDetectionActive) {
platforms.forEach((platform) => {
platform.position.x -= 5;
});
checkpoints.forEach((checkpoint) => {
checkpoint.position.x -= 5;
});
} else if (keys.leftKey.pressed && isCheckpointCollisionDetectionActive) {
platforms.forEach((platform) => {
platform.position.x += 5;
});
checkpoints.forEach((checkpoint) => {
checkpoint.position.x += 5;
});
}
}
platforms.forEach((platform) => {
const collisionDetectionRules = [
player.position.y + player.height <= platform.position.y,
player.position.y + player.height + player.velocity.y >= platform.position.y,
player.position.x >= platform.position.x - player.width / 2,
player.position.x <=
platform.position.x + platform.width - player.width / 3,
];
if (collisionDetectionRules.every((rule) => rule)) {
player.velocity.y = 0;
return;
}
const platformDetectionRules = [
player.position.x >= platform.position.x - player.width / 2,
player.position.x <=
platform.position.x + platform.width - player.width / 3,
player.position.y + player.height >= platform.position.y,
player.position.y <= platform.position.y + platform.height,
];
if (platformDetectionRules.every(rule => rule)) {
player.position.y = platform.position.y + player.height;
player.velocity.y = gravity;
};
});
checkpoints.forEach((checkpoint, index, checkpoints) => {
const checkpointDetectionRules = [
player.position.x >= checkpoint.position.x,
player.position.y >= checkpoint.position.y,
player.position.y + player.height <=
checkpoint.position.y + checkpoint.height,
isCheckpointCollisionDetectionActive,
player.position.x - player.width <=
checkpoint.position.x - checkpoint.width + player.width * 0.9,
index === 0 || checkpoints[index - 1].claimed === true,
];
if (checkpointDetectionRules.every((rule) => rule)) {
checkpoint.claim();
--fcc-editable-region--
--fcc-editable-region--
};
});
}
const keys = {
rightKey: {
pressed: false
},
leftKey: {
pressed: false
}
};
const movePlayer = (key, xVelocity, isPressed) => {
if (!isCheckpointCollisionDetectionActive) {
player.velocity.x = 0;
player.velocity.y = 0;
return;
}
switch (key) {
case "ArrowLeft":
keys.leftKey.pressed = isPressed;
if (xVelocity === 0) {
player.velocity.x = xVelocity;
}
player.velocity.x -= xVelocity;
break;
case "ArrowUp":
case " ":
case "Spacebar":
player.velocity.y -= 8;
break;
case "ArrowRight":
keys.rightKey.pressed = isPressed;
if (xVelocity === 0) {
player.velocity.x = xVelocity;
}
player.velocity.x += xVelocity;
}
}
const startGame = () => {
canvas.style.display = "block";
startScreen.style.display = "none";
animate();
}
const showCheckpointScreen = (msg) => {
checkpointScreen.style.display = "block";
checkpointMessage.textContent = msg;
if (isCheckpointCollisionDetectionActive) {
setTimeout(() => (checkpointScreen.style.display = "none"), 2000);
}
};
startBtn.addEventListener("click", startGame);
window.addEventListener("keydown", ({ key }) => {
movePlayer(key, 8, true);
});
window.addEventListener("keyup", ({ key }) => {
movePlayer(key, 0, false);
});