--- id: 64cb3f62b10c336bada1c70c title: Step 102 challengeType: 0 dashedName: step-102 --- # --description-- Inside the animate function, you will need to draw each of the `checkpoints` onto the canvas. Add a `forEach` loop that iterates through the `checkpoints` array. Inside the callback function, add a `checkpoint` parameter and for the body of the function call the `draw` method on each `checkpoint`. # --hints-- You should have a `forEach` loop that iterates through the `checkpoints` array. ```js assert.match(code, /\bcheckpoints\b\s*\.\s*forEach\s*\(/s); ``` You should have a `checkpoint` parameter inside the `forEach` callback function. ```js assert.match(code, /\s*checkpoints\s*\.\s*forEach\s*\(\s*(\(\s*checkpoint\s*\)|checkpoint)\s*=>\s*{?/s); ``` You should call the `draw` method on each `checkpoint` inside the `forEach` callback function. ```js assert.match(code, /\s*checkpoints\.forEach\s*\(\s*(\(\s*checkpoint\s*\)|checkpoint)\s*=>\s*(\{\s*checkpoint\.draw\(\s*\)\s*;?\s*\}|checkpoint\.draw\(\s*\))\s*\)\s*;?/s); ``` # --seed-- ## --seed-contents-- ```html Learn Intermediate OOP by Building a Platformer Game

freeCodeCamp Code Warrior

Help the main player navigate to the yellow checkpoints.

Use the keyboard arrows to move the player around.

You can also use the spacebar to jump.

Congrats!

You reached the last checkpoint.

``` ```css * { 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; } } ``` ```js 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 - 2 * this.width) { this.position.x = canvas.width - 2 * this.width; } } } 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(); }); --fcc-editable-region-- --fcc-editable-region-- 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; }); } else if (keys.leftKey.pressed && isCheckpointCollisionDetectionActive) { platforms.forEach((platform) => { platform.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; }; }); } 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(); } startBtn.addEventListener("click", startGame); window.addEventListener("keydown", ({ key }) => { movePlayer(key, 8, true); }); window.addEventListener("keyup", ({ key }) => { movePlayer(key, 0, false); }); ```