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