From 243d0bb83ca18b97ae0ad952ed68028adf9c489b Mon Sep 17 00:00:00 2001 From: Senanu Adjabeng Date: Sun, 30 Jun 2024 12:31:41 +0200 Subject: [PATCH] My lab Solution --- README.md | 61 ++++++++++----------------- index.html | 3 ++ js/components.js | 24 +++++++++++ js/game.js | 107 ++++++++++++++++++++++++++++++++++++++++++++++- js/obstacle.js | 36 ++++++++++++++++ js/player.js | 70 +++++++++++++++++++++++++++++++ js/script.js | 49 ++++++++++++++++++++++ 7 files changed, 309 insertions(+), 41 deletions(-) create mode 100644 js/components.js create mode 100644 js/obstacle.js create mode 100644 js/player.js diff --git a/README.md b/README.md index 300abcf..8508e82 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ # LAB | DOM Race Car - ![Island Racer Logo](images/logo.png)
@@ -79,7 +78,7 @@ The start screen is already displayed on the page, as shown below. ![island racer game start screen](https://education-team-2020.s3.eu-west-1.amazonaws.com/web-dev/m1/lab-dom-race-car/lab-dom-race-car-start-screen.png) -Upon clicking the **Start Game** button, the player should transition from the _start screen_ to the _game screen_, initiating the game. +Upon clicking the **Start Game** button, the player should transition from the _start screen_ to the _game screen_, initiating the game. In the next iteration, we will create the `Game` class and implement the functionality required to **start** the game. @@ -173,7 +172,7 @@ class Game { this.lives = 3; this.gameIsOver = false; this.gameIntervalId; - this.gameLoopFrequency = Math.round(1000/60); // 60fps + this.gameLoopFrequency = Math.round(1000 / 60); // 60fps } start() { @@ -183,24 +182,24 @@ class Game { // Hide the start screen this.startScreen.style.display = "none"; - + // Show the game screen this.gameScreen.style.display = "block"; // Runs the gameLoop on a fequency of 60 times per second. Also stores the ID of the interval. this.gameIntervalId = setInterval(() => { - this.gameLoop() - }, this.gameLoopFrequency) + this.gameLoop(); + }, this.gameLoopFrequency); } gameLoop() { console.log("in the game loop"); - + this.update(); // If "gameIsOver" is set to "true" clear the interval to stop the loop if (this.gameIsOver) { - clearInterval(this.gameIntervalId) + clearInterval(this.gameIntervalId); } } @@ -281,11 +280,12 @@ In this iteration, we will create the `Player` class, representing the player's - `1`: moving horizontally to the right - `-1`: moving horizontally to the left - `directionY` - initially set to 0. It is used to specify the **vertical** movement direction and can have the following values: + - `0`: not moving vertically - `1`: moving vertically down - `-1`: moving vertically up - - `element` - the **image** element representing the car. This image element should be created in the constructor using the *provided image source (image url) passed as an argument* to the constructor. + - `element` - the **image** element representing the car. This image element should be created in the constructor using the _provided image source (image url) passed as an argument_ to the constructor.
@@ -404,8 +404,6 @@ class Player { return false; } } - - } ``` @@ -417,7 +415,7 @@ class Player { ## Iteration 4: Add the Player to the Game -1. As a reminder, we have already defined the `player` property of the `Game` class and set it to `null`. Now let's instantiate a new `Player` object and store it in the `player` property of the `Game`. +1. As a reminder, we have already defined the `player` property of the `Game` class and set it to `null`. Now let's instantiate a new `Player` object and store it in the `player` property of the `Game`.
See the code @@ -472,19 +470,15 @@ class Game {
- - ![island racer game - player car showing](https://education-team-2020.s3.eu-west-1.amazonaws.com/web-dev/m1/lab-dom-race-car/lab-dom-race-car-player.png) - -
## Iteration 5: Handle Keyboard Input The goal of this iteration is to allow the player to control the car using the keyboard. -To do this, we will add an event listener in the `js/script.js` file, which will update the player's car `directionX` and `directionY` properties based on the keys that the user presses on the keyboard. +To do this, we will add an event listener in the `js/script.js` file, which will update the player's car `directionX` and `directionY` properties based on the keys that the user presses on the keyboard. This function listens for the `keydown` event using `document.onkeydown` and checks if the pressed key matches any of the allowed keystrokes (arrow keys).
@@ -580,7 +574,7 @@ In this iteration, we will create the `Obstacle` class, which will be used to cr
move() - - Move the obstacle down by 3px by continuously updating its `top` property. + - Move the obstacle down by 3px by continuously updating its `top` property. - Update the obstacle's position on the screen by calling the `updatePosition()` method. @@ -666,16 +660,15 @@ This method is responsible for updating the game state during each loop iteratio - Check if the player has run out of lives, and end the game if so. Create a new method (`endGame`) responsible for ending the game. -
endGame() - - Remove a player and all the obstacles from the DOM. - - Set the `gameIsOver` flag to `true`. - - Hide the game screen. - - Show the end game screen. +- Remove a player and all the obstacles from the DOM. +- Set the `gameIsOver` flag to `true`. +- Hide the game screen. +- Show the end game screen.
@@ -740,7 +733,7 @@ class Game { // Create a new method responsible for ending the game endGame() { this.player.element.remove(); - this.obstacles.forEach(obstacle => obstacle.element.remove()); + this.obstacles.forEach((obstacle) => obstacle.element.remove()); this.gameIsOver = true; @@ -760,14 +753,10 @@ class Game {
- -
- - ## Iteration 8: End Game Screen In this final iteration, we will implement the end game screen, shown to the user when the game is over. @@ -796,9 +785,10 @@ window.onload = function () { }); // The function that reloads the page to start a new game - function restartGame() { - location.reload(); - } + restartButton.addEventListener("click", function () { + // Call the restartGame function when the button is clicked + restartGame(); + }); }; ``` @@ -910,7 +900,6 @@ class Player extends Component { } } } - ```
@@ -936,17 +925,13 @@ class Obstacle extends Component { // Update the obstacle's position on the screen this.updatePosition(); } - } - ```
- -
## BONUS - Iteration 10: Points, points, points @@ -959,7 +944,7 @@ To make the game more competitive, add elements to shows the player's score and ## Lab Solution -You can find the complete solution code for the lab at: [dom-race-car](https://github.com/ironhack-labs/lesson-code-dom-race-car). +You can find the complete solution code for the lab at: [dom-race-car](https://github.com/ironhack-labs/lesson-code-dom-race-car). To clone the solution repository, run the following commands: @@ -971,8 +956,6 @@ git clone https://github.com/ironhack-labs/lesson-code-dom-race-car.git cd lesson-code-dom-race-car ``` - -
**Happy coding!** :heart: diff --git a/index.html b/index.html index d80d77c..5db2f73 100644 --- a/index.html +++ b/index.html @@ -39,5 +39,8 @@

Game Over

+ + + diff --git a/js/components.js b/js/components.js new file mode 100644 index 0000000..16906bb --- /dev/null +++ b/js/components.js @@ -0,0 +1,24 @@ +class Component { + constructor(gameScreen, left, top, width, height, imgSrc) { + this.gameScreen = gameScreen; + this.left = left; + this.top = top; + this.width = width; + this.height = height; + this.element = document.createElement("img"); + + this.element.src = imgSrc; + this.element.style.position = "absolute"; + this.element.style.width = `${width}px`; + this.element.style.height = `${height}px`; + this.element.style.left = `${left}px`; + this.element.style.top = `${top}px`; + + this.gameScreen.appendChild(this.element); + } + + updatePosition() { + this.element.style.left = `${this.left}px`; + this.element.style.top = `${this.top}px`; + } +} diff --git a/js/game.js b/js/game.js index af4789c..2a72831 100644 --- a/js/game.js +++ b/js/game.js @@ -1,3 +1,106 @@ class Game { - // code to be added -} \ No newline at end of file + constructor() { + this.startScreen = document.getElementById("game-intro"); + this.gameScreen = document.getElementById("game-screen"); + this.gameEndScreen = document.getElementById("game-end"); + this.player = new Player( + this.gameScreen, + 200, + 500, + 100, + 150, + "./images/car.png" + ); + this.height = 600; + this.width = 500; + this.obstacles = []; + this.score = 0; + this.lives = 3; + this.gameIsOver = false; + this.gameIntervalId; + this.gameLoopFrequency = Math.round(1000 / 60); // 60fps + } + + start() { + // Set the height and width of the game screen + this.gameScreen.style.height = `${this.height}px`; + this.gameScreen.style.width = `${this.width}px`; + + // Hide the start screen + this.startScreen.style.display = "none"; + // Show the game screen + this.gameScreen.style.display = "block"; + + // Executes the gameLoop on a fequency of 60 times per second. Also stores the ID of the interval. + this.gameIntervalId = setInterval(() => { + this.gameLoop(); + }, this.gameLoopFrequency); + } + + gameLoop() { + console.log("in the game loop"); + + this.update(); + + if (this.gameIsOver) { + clearInterval(this.gameIntervalId); + } + } + + update() { + this.player.move(); + + // Check for collision and if an obstacle is still on the screen + for (let i = 0; i < this.obstacles.length; i++) { + const obstacle = this.obstacles[i]; + obstacle.move(); + + // If the player's car collides with an obstacle + if (this.player.didCollide(obstacle)) { + // Remove the obstacle element from the DOM + obstacle.element.remove(); + // Remove obstacle object from the array + this.obstacles.splice(i, 1); + // Reduce player's lives by 1 + this.lives--; + // Update the counter variable to account for the removed obstacle + i--; + } // If the obstacle is off the screen (at the bottom) + else if (obstacle.top > this.height) { + // Increase the score by 1 + this.score++; + // Remove the obstacle from the DOM + obstacle.element.remove(); + // Remove obstacle object from the array + this.obstacles.splice(i, 1); + // Update the counter variable to account for the removed obstacle + i--; + } + } + + // If the lives are 0, end the game + if (this.lives === 0) { + this.endGame(); + } + + // Create a new obstacle based on a random probability + // when there is no other obstacles on the screen + if (Math.random() > 0.98 && this.obstacles.length < 1) { + this.obstacles.push(new Obstacle(this.gameScreen)); + } + } + + // Create a new method responsible for ending the game + endGame() { + this.player.element.remove(); + this.obstacles.forEach(function (obstacle) { + obstacle.element.remove(); + }); + + this.gameIsOver = true; + // Hide game screen + this.gameScreen.style.display = "none"; + // Show end game screen + this.gameEndScreen.style.display = "block"; + } +} diff --git a/js/obstacle.js b/js/obstacle.js new file mode 100644 index 0000000..9e505db --- /dev/null +++ b/js/obstacle.js @@ -0,0 +1,36 @@ +class obstacle extends Component { + constructor(gameScreen) { + super( + gameScreen, + Math.floor(Math.random() * 300 + 70), + 0, + 100, + 150, + "./images/redCar.png" + ); + this.gameScreen = gameScreen; + this.left = Math.floor(Math.random() * 300 + 70); + this.top = 0; + this.width = 100; + this.height = 150; + this.element = document.createElement("img"); + + this.element.src = "./images/redCar.png"; + this.element.style.position = "absolute"; + this.element.style.left = `${this.left}px`; + this.element.style.top = `${this.right}px`; + this.element.style.width = `${this.width}px`; + this.element.style.height = `${this.height}px`; + + this.gameScreen.appendChild(this.element); + } + updatePosition() { + this.element.style.left = `${this.left}px`; + this.element.style.top = `${this.top}px`; + } + + move() { + this.top += 3; + this.updatePosition(); + } +} diff --git a/js/player.js b/js/player.js new file mode 100644 index 0000000..0444019 --- /dev/null +++ b/js/player.js @@ -0,0 +1,70 @@ +class Player extends Component { + constructor(gameScreen, left, top, width, height, imgSrc) { + super(gameScreen, left, top, width, height, imgSrc); + + // this.directionX = 0; + // this.directionY = 0; + + this.gameScreen = gameScreen; + this.left = left; + this.top = top; + this.width = width; + this.height = height; + this.element = document.createElement("img"); + this.directionX = 0; + this.directionY = 0; + + this.element.src = imgSrc; + this.element.style.position = "absolute"; + this.element.style.width = `${width}px`; + this.element.style.height = `${height}px`; + this.element.style.left = `${left}px`; + this.element.style.top = `${top}px`; + + this.gameScreen.appendChild(this.element); + } + + move() { + this.left += this.directionX; + this.top += this.directionY; + + if (this.left < 10) { + this.left = 10; + } + + if (this.top < 10) { + this.top = 10; + } + + if (this.left > this.gameScreen.offsetWidth - this.width - 10) { + this.left = this.gameScreen.offsetWidth - this.width - 10; + } + + if (this.top > this.gameScreen.offsetHeight - this.height - 10) { + this.top = this.gameScreen.offsetHeight - this.height - 10; + } + + this.updatePosition(); + } + + updatePosition() { + this.element.style.left = `${this.left}px`; + this.element.style.top = `${this.top}px`; + } + + didCollide(obstacle) { + const playerRect = this.element.getBoundingClientRect(); + const obstacleRect = obstacle.element.getBoundingClientRect(); + + if ( + playerRect.left < obstacleRect.right && + playerRect.right > obstacleRect.left && + playerRect.top < obstacleRect.bottom && + playerRect.bottom > obstacleRect.top + ) { + return true; + } else { + return false; + } + } +} diff --git a/js/script.js b/js/script.js index 95e544f..96c61b0 100644 --- a/js/script.js +++ b/js/script.js @@ -1,12 +1,61 @@ window.onload = function () { const startButton = document.getElementById("start-button"); const restartButton = document.getElementById("restart-button"); + let game; startButton.addEventListener("click", function () { startGame(); }); + restartButton.addEventListener("click", function () { + // Call the restartGame function when the button is clicked + restartGame(); + }); + function startGame() { console.log("start game"); + game = new Game(); + + game.start(); + } + + // The function that reloads the page to start a new game + function restartGame() { + location.reload(); } + + // Function that handles keydown event + function handleKeydown(event) { + const key = event.key; + const possibleKeystrokes = [ + "ArrowLeft", + "ArrowUp", + "ArrowRight", + "ArrowDown", + ]; + + // Check if the pressed key is in the possibleKeystrokes array + if (possibleKeystrokes.includes(key)) { + event.preventDefault(); + + // Update player's directionX and directionY based on the key pressed + switch (key) { + case "ArrowLeft": + game.player.directionX = -1; + break; + case "ArrowUp": + game.player.directionY = -1; + break; + case "ArrowRight": + game.player.directionX = 1; + break; + case "ArrowDown": + game.player.directionY = 1; + break; + } + } + } + + // Add the handleKeydown function as an event listener for the keydown event + window.addEventListener("keydown", handleKeydown); };