From 0f97c36655c2f91dd80001f567fada46a56bafb6 Mon Sep 17 00:00:00 2001 From: Beloise Huffstutler Date: Mon, 25 Mar 2024 16:08:38 -0500 Subject: [PATCH] Adds building a platformer game --- building-a-platformer-game/index.html | 35 ++++ building-a-platformer-game/script.js | 287 ++++++++++++++++++++++++++ building-a-platformer-game/styles.css | 91 ++++++++ 3 files changed, 413 insertions(+) create mode 100644 building-a-platformer-game/index.html create mode 100644 building-a-platformer-game/script.js create mode 100644 building-a-platformer-game/styles.css diff --git a/building-a-platformer-game/index.html b/building-a-platformer-game/index.html new file mode 100644 index 0000000..860a09c --- /dev/null +++ b/building-a-platformer-game/index.html @@ -0,0 +1,35 @@ + + + + + + + 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.

+
+ + + + + + diff --git a/building-a-platformer-game/script.js b/building-a-platformer-game/script.js new file mode 100644 index 0000000..ef67e1f --- /dev/null +++ b/building-a-platformer-game/script.js @@ -0,0 +1,287 @@ +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(); + }); + + 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(); + + + if (index === checkpoints.length - 1) { + isCheckpointCollisionDetectionActive = false; + showCheckpointScreen("You reached the final checkpoint!"); + movePlayer("ArrowRight", 0, false); + } else if (player.position.x >= checkpoint.position.x && player.position.x <= checkpoint.position.x + 40) { + showCheckpointScreen("You reached a checkpoint!") + } + + + }; + }); +} + + +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); +}); diff --git a/building-a-platformer-game/styles.css b/building-a-platformer-game/styles.css new file mode 100644 index 0000000..aa50484 --- /dev/null +++ b/building-a-platformer-game/styles.css @@ -0,0 +1,91 @@ +* { + 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; + } + } + \ No newline at end of file