skip to Main Content

I’m making a simple 2d endless runner game for a project. The game is simple,

  • it has one infinitely repeating obstacle,
  • it is supposed to have 3 difficulty levels that the player can choose.
  • The score starts at 0
  • the score updates when a collision is detected above the obstacle
  • ends the game when the player hits the obstacle.

(thanks to kikon for helping i modified your code a little)

I do have some other problems with my code I need to fix as well, but all help is appreciated. So if any of you seeing this spot any extra errors or questionable logic then please let me know.

Very sorry if it’s just a small syntax error I missed. Here’s the code:

const player = document.getElementById('player');
const obstacle = document.getElementById('obstacle');
const scoreValue = document.getElementById('score-value');
let score = 0;
let gameRunning = false;
let hasJumpedOver = false;

function updateScore() {
  score++;
  scoreValue.innerText = score;
}

function gameOver() {
  gameRunning = false;
  alert('Game Over! Your score is ' + score);
}

function moveObstacle() {
  if (!gameRunning) return;

  let obstaclePosition = parseInt(window.getComputedStyle(obstacle).getPropertyValue('right')) + speed;

  if (obstaclePosition < window.innerWidth) {
    obstacle.style.right = `${obstaclePosition + speed}px`;
  } else {
    obstacle.style.right = '0';
    hasJumpedOver = false; // Reset this flag when the obstacle resets
  }

  detectCollision();
}

function jump() {
  if (!player.classList.contains('jump')) {
    player.classList.add('jump');

    setTimeout(() => {
      player.classList.remove('jump');
    }, 500);
  }
}

function detectCollision() {
  let playerRect = player.getBoundingClientRect();
  let obstacleRect = obstacle.getBoundingClientRect();

  if (playerRect.left < obstacleRect.right &&
    playerRect.right > obstacleRect.left &&
    playerRect.top < obstacleRect.bottom &&
    playerRect.bottom > obstacleRect.top) {
    // Collision detected, end the game
    gameOver();
  } else if (playerRect.right < obstacleRect.left && !hasJumpedOver) {
    // Player has successfully jumped over the obstacle
    hasJumpedOver = true;
    updateScore();
  }
}

document.addEventListener('keydown', (event) => {
  if (event.code === 'Space') {
    jump();
  }
});

setInterval(moveObstacle, 20);

const difficultyButtons = document.querySelectorAll('#difficulty-buttons button');

difficultyButtons.forEach(button => {
  button.addEventListener('click', function() {
    // remove selected class from all buttons
    difficultyButtons.forEach(btn => {
      btn.classList.remove('button-selected');
    });

    // add the selected class to clicked button
    this.classList.add('button-selected');
  });
});


// misc code for fade in and out on same page

document.getElementById('start-button').addEventListener('click', function() {
  document.getElementById('game-container').classList.add('visible');
  document.getElementById('main-page').classList.add('fade-out-up');
  gameRunning = true; // game starts when start button gets clicked
});
body {
  margin: 0;
  overflow: hidden;
}

#main-page {
  z-index: 1;
}

#game-container {
  position: absolute;
  width: 100%;
  height: 100vh;
  opacity: 0;
  transition: opacity 2s ease-in-out;
  background-image: url('background.jpg');
  background-repeat: no-repeat;
  background-size: big;
  background-position: center center;
}

/*for game container fade in */
#game-container.visible {
  opacity: 1;
}

/*stuff for main elements of the game*/
#player {
  position: absolute;
  bottom: 0;
  left: 50px;
  width: 50px;
  height: 50px;
  background-color: pink;
}

#obstacle {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 50px;
  height: 50px;
  background-color: grey;
}

#score {
  position: relative;
  top: 15px;
  left: 10px;
  font-size: 25px;
  text-align: center;
  font-family: sans-serif;
  color: aliceblue;
}


/*for jump*/

.jump {
  transform: translateY(-100px);
}

/*for the main page div to fade out*/
.fade-out-up {
  animation: fadeOutUp 1s forwards;
}

@keyframes fadeOutUp {
  0% {
    opacity: 1;
    transform: translateY(0);
  }
  100% {
    opacity: 0;
    transform: translateY(-20px);
  }
}

/*for difficulty selectors */
.button-selected {
  transform: scale(0.9);
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Endless Runner Game</title>
  <link rel="stylesheet" href="style.css">
  <link rel="stylesheet" href="w3.css">
</head>

<body>
  <div id="main-page" class="w3-container">
    <h1 class="">Cube Runner 2D</h1>
    <p>Cube Runner 2D is an endless 2D runner game in where the object of the game is to get your score as high as possible before you run into the obstacle (you die).
    </p>
    <p>The game has <span style="font-weight: bold;">Three Difficulty levels </span>you can choose from below,
      <span class="w3-text-green" style="font-weight: bold;">Easy, </span>
      <span class="w3-text-yellow" style="font-weight: bold;">Medium, </span>and
      <span class="w3-text-red" style="font-weight: bold;">Hard.</span></p>
    <div id="difficulty-buttons">
      <button onclick="setDifficulty('easy')" class="w3-green">Easy</button>
      <button onclick="setDifficulty('medium')" class="w3-yellow">Medium</button>
      <button onclick="setDifficulty('hard')" class="w3-red">Hard</button>
    </div>
    <button id="start-button" class="w3-display-middle">Start Game</button>
  </div>
  <div id="game-container">
    <div id="player"></div>
    <div id="obstacle"></div>
    <div id="score">Score: <span id="score-value">-1</span></div>
    <script src="script.js"></script>
  </div>
</body>

</html>

2

Answers


  1. There are a lot of issues which I won’t get into for the sake of brevity, but let’s try to solve the ones that might be blocking you so that you can continue learning:

    1. You forgot to define speed in your game loop moveObstacle()
    2. setDifficulty() you’re trying to use in your index.html is not defined
    3. As far as I can see, the obstacle is not being reset after losing the game

    Try this:

    const obstacle = document.getElementById('obstacle');
    const scoreValue = document.getElementById('score-value');
    let score = 0;
    let gameRunning = false;
    let hasJumpedOver = false;
    let difficulty = 'easy';
    let speed = 10;
    
    function updateScore() {
      score++;
      speed++;
      scoreValue.innerText = score;
    }
    
    function gameOver() {
      gameRunning = false;
      alert('Game Over! Your score is ' + score);
      obstacle.style.right = '0';
    }
    
    function moveObstacle() {
      if (!gameRunning) return;
    
      let obstaclePosition = parseInt(window.getComputedStyle(obstacle).getPropertyValue('right')) + speed;
    
      if (obstaclePosition < window.innerWidth) {
        obstacle.style.right = `${obstaclePosition + speed}px`;
      } else {
        obstacle.style.right = '0';
        hasJumpedOver = false; // Reset this flag when the obstacle resets
      }
    
      detectCollision();
    }
    
    function jump() {
      if (!player.classList.contains('jump')) {
        player.classList.add('jump');
    
        setTimeout(() => {
          player.classList.remove('jump');
        }, 500);
      }
    }
    
    function detectCollision() {
      let playerRect = player.getBoundingClientRect();
      let obstacleRect = obstacle.getBoundingClientRect();
    
      if (playerRect.left < obstacleRect.right &&
        playerRect.right > obstacleRect.left &&
        playerRect.top < obstacleRect.bottom &&
        playerRect.bottom > obstacleRect.top) {
        // Collision detected, end the game
        gameOver();
      } else if (playerRect.right < obstacleRect.left && !hasJumpedOver) {
        // Player has successfully jumped over the obstacle
        hasJumpedOver = true;
        updateScore();
      }
    }
    
    document.addEventListener('keydown', (event) => {
      if (event.code === 'Space') {
        jump();
      }
    });
    
    setInterval(moveObstacle, 20);
    
    function setDifficulty(difficulty) {
      switch (difficulty) {
        case 'easy':
          speed = speed;
          break;
        case 'medium':
          speed = 15;
          break;
        case 'hard':
          speed = 25;
          break;
        default:
          break;
      }
    
    }
    
    // misc code for fade in and out on same page
    
    document.getElementById('start-button').addEventListener('click', function() {
      document.getElementById('game-container').classList.add('visible');
      document.getElementById('main-page').classList.add('fade-out-up');
      gameRunning = true; // game starts when start button gets clicked
    });
    
    
    Login or Signup to reply.
  2. As others indicated a number of issues with undeclared variable/values, issues with the start/stop/restart code; speed is still messed up here and does not reset properly IF you were to choose a new one etc.

    Here I simply "made it work" albeit with probably terrible UI/usability etc.

    I placed all your game specific global variables in a game namespace object as properties and then used this to reference them in the functions. The other parts could also be placed within this but I left that our for you as a learning exercise.

    I also added a simple message function to show what was happening as it occurred a bit.

    var game = {
      /* to avoid console logs mashing screen only */
      testResults: document.getElementById('test-results'),
      player: document.getElementById('player'),
      obstacle: document.getElementById('obstacle'),
      scoreValue: document.getElementById('score-value'),
      score: 0,
      gameRunning: false,
      hasJumpedOver: false,
      speed: 20,
      updateScore: function() {
        this.score++;
        this.scoreValue.innerText = this.score;
        this.showResults(`Upd Your score is: ${this.score}`);
      },
      startMove: function() {
        this.gameRunning = true; // game starts when start button gets clicked
        setInterval(() => {
          this.moveObstacle();
        }, 200);
        this.showResults('started');
      },
      showResults: function(text) {
        this.testResults.textContent = text;
      },
      moveObstacle: function() {
        if (!this.gameRunning) return;
        let obstaclePosition = parseInt(window.getComputedStyle(this.obstacle).getPropertyValue('right')) + this.speed;
        this.showResults(`moveOb: ${obstaclePosition} ${this.speed} ${window.innerWidth}`);
        if (obstaclePosition < window.innerWidth) {
          this.obstacle.style.right = (obstaclePosition + this.speed) + "px";
        } else {
          this.obstacle.style.right = '0';
          this.hasJumpedOver = false; // Reset this flag when the obstacle resets
        }
        this.detectCollision();
      },
      gameOver: function gameOver() {
        this.gameRunning = false;
        this.showResults(`Game Over! Your score is: ${this.score}`);
        alert('Game Over! Your score is ' + this.score);
      },
      jump: function jump() {
        if (!this.player.classList.contains('jump')) {
          this.player.classList.add('jump');
          setTimeout(() => {
            this.player.classList.remove('jump');
          }, 500);
        }
      },
      detectCollision: function detectCollision() {
        let playerRect = this.player.getBoundingClientRect();
        let obstacleRect = this.obstacle.getBoundingClientRect();
        if (playerRect.left < obstacleRect.right &&
          playerRect.right > obstacleRect.left &&
          playerRect.top < obstacleRect.bottom &&
          playerRect.bottom > obstacleRect.top) {
          // Collision detected, end the game
          this.gameOver();
        } else if (playerRect.right < obstacleRect.left && !this.hasJumpedOver) {
          // Player has successfully jumped over the obstacle
          this.hasJumpedOver = true;
          this.showResults(`SV Your score is: ${this.score} ${this.speed}`);
          this.updateScore();
        }
      }
    };
    
    document.addEventListener('keydown', (event) => {
      if (event.code === 'Space') {
        game.jump();
      }
    });
    
    const difficultyContainer = document.querySelector('#difficulty-buttons');
    const difficultyChoices = difficultyContainer.querySelectorAll('.speed-choice');
    
    [...difficultyChoices].forEach(choice => {
      choice.addEventListener('click', function(event) {
        // remove selected class from all buttons
        const choices = event.target.closest('.choice-container')
          .querySelectorAll('.speed-choice');
        [...choices].forEach(btn => {
          btn.classList.remove('button-selected');
        });
        // add the selected class to clicked button
        event.target.classList.add('button-selected');
      });
    });
    
    // misc code for fade in and out on same page
    document.getElementById('start-button')
      .addEventListener('click', function(event) {
        document.getElementById('game-container').classList.add('visible');
        document.getElementById('main-page').classList.add('fade-out-up');
        game.startMove();
      });
    body {
      margin: 0;
      overflow: hidden;
    }
    
    #main-page {
      z-index: 1;
    }
    
    #game-container {
      /*  position: absolute;*/
      width: 100%;
      height: 100vh;
      opacity: 0;
      transition: opacity 2s ease-in-out;
      background-image: url('background.jpg');
      background-repeat: no-repeat;
      background-size: big;
      background-position: center center;
    }
    
    #game-container.visible {
      opacity: 1;
    }
    
    #player {
      position: absolute;
      bottom: 0;
      left: 50px;
      width: 50px;
      height: 50px;
      background-color: pink;
    }
    
    #obstacle {
      position: absolute;
      bottom: 0;
      right: 0;
      width: 50px;
      height: 50px;
      background-color: grey;
    }
    
    #score {
      position: relative;
      top: 15px;
      left: 10px;
      font-size: 25px;
      text-align: center;
      font-family: sans-serif;
      color: aliceblue;
    }
    
    .jump {
      transform: translateY(-100px);
    }
    
    .fade-out-up {
      animation: fadeOutUp 1s forwards;
    }
    
    @keyframes fadeOutUp {
      0% {
        opacity: 1;
        transform: translateY(0);
      }
      100% {
        opacity: 0;
        transform: translateY(-20px);
      }
    }
    
    .button-selected {
      transform: scale(0.9);
      background-color: #00FF0020;
    }
    <div id="test-results">&nbsp;</div>
    <div id="main-page" class="w3-container">
      <h1 class="">Cube Runner 2D</h1>
      <p>Cube Runner 2D is an endless 2D runner game in where the object of the game is to get your score as high as possible before you run into the obstacle (you die).
      </p>
      <p>The game has <span style="font-weight: bold;">Three Difficulty levels </span>you can choose from below,
        <span class="w3-text-green" style="font-weight: bold;">Easy, </span>
        <span class="w3-text-yellow" style="font-weight: bold;">Medium, </span>and
        <span class="w3-text-red" style="font-weight: bold;">Hard.</span></p>
      <div id="difficulty-buttons" class="choice-container">
        <button data-difficulty='easy' class="speed-choice w3-green">Easy</button>
        <button data-difficulty='medium' class="speed-choice w3-yellow">Medium</button>
        <button data-difficulty='hard' class="speed-choice w3-red">Hard</button>
      </div>
      <button id="start-button" class="w3-display-middle">Start Game</button>
    </div>
    <div id="game-container">
      <div id="player"></div>
      <div id="obstacle"></div>
      <div id="score">Score: <span id="score-value">-1</span></div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search