-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ed250a9
commit 0f97c36
Showing
3 changed files
with
413 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}); |
Oops, something went wrong.