Search code examples
javascripthtml2d-games

JavaScript Snake Game with two Snakes (Local Mutiplayer)


Im programing a Snake Webgame with HTML, CSS and JavaScript and im implementing Multiple Gamemodes, one should be a Local Multiplayer where one Person is playing with Arrow keys and the Other Person with WASD.

But I got the problem, that I dont know how to Summon a Second Snake. I tried to just copy the Summon Code and rename the variables. But that didn't work, no matter what I tryed.

The code is 90% made by myself, but because of some JavaScript beginner issues I needed some help by the web.

Can someone may tell me how I can summon a Second snake? I just need the "how to" Summon.

let canvas = document.getElementById('game');
let ctx = canvas.getContext('2d');

let grid = 10;
let count = 0;

//Scores
let Score = 0;
let HighScore = 0;

let Alive = true;

//Snake Speed
let Speed = 15;

//Snake Base Stats
let snake = {
  x: 200,
  y: 200,
  dx: grid,
  dy: 0,
  cells: [],
  maxCells: 4
};

//First Apple Spawn
let apple = {
  x: 320,
  y: 320
};

//Random Int for Apple Spawn
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min)) + min;
}

function loop() {
  requestAnimationFrame(loop);

  //Tic speed
  if (++count < Speed) {
    return;
  }

  count = 0;
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  snake.x += snake.dx;
  snake.y += snake.dy;

  //Automatic Movement
  if (snake.x < 0) {
    snake.x = canvas.width - grid;
  } else if (snake.x >= canvas.width) {
    snake.x = 0;
  }

  if (snake.y < 0) {
    snake.y = canvas.height - grid;
  } else if (snake.y >= canvas.height) {
    snake.y = 0;
  }


  snake.cells.unshift({
    x: snake.x,
    y: snake.y
  });

  if (snake.cells.length > snake.maxCells) {
    snake.cells.pop();
  }

  //Apple Color
  ctx.fillStyle = 'gold';
  ctx.fillRect(apple.x, apple.y, grid - 1, grid - 1);

  //Snake Color
  ctx.fillStyle = 'white';
  snake.cells.forEach(function(cell, index) {
    //Snake Color
    ctx.fillRect(cell.x, cell.y, grid - 1, grid - 1);

    //Snake Eat Apple
    if (cell.x === apple.x && cell.y === apple.y) {
      snake.maxCells++;
      apple.x = getRandomInt(0, 25) * grid;
      apple.y = getRandomInt(0, 25) * grid;
      SnakeScore();
    }

    //Snake Dies
    for (var i = index + 1; i < snake.cells.length; i++) {

      if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) {

        SummonSnake();
        Alive = false;
        SnakeScore();
      }
    }
  });
}


//Arrow Key Movement
document.addEventListener('keydown', function(e) {

  if (e.which === 37 && snake.dx === 0) {
    snake.dx = -grid;
    snake.dy = 0;
  } else if (e.which === 38 && snake.dy === 0) {
    snake.dy = -grid;
    snake.dx = 0;
  } else if (e.which === 39 && snake.dx === 0) {
    snake.dx = grid;
    snake.dy = 0;
  } else if (e.which === 40 && snake.dy === 0) {
    snake.dy = grid;
    snake.dx = 0;
  }
});

requestAnimationFrame(loop);

//Score
function SnakeScore() {

  if (Alive === true) {
    Score++;
    document.getElementById("Score").innerHTML = Score;
  } else if (Alive === false) {
    Score = 0;
    document.getElementById("Score").innerHTML = Score;
    Alive = true;
  }
  if (Score > HighScore) {
    SnakeHighscore();
  }
}

//Highscore
function SnakeHighscore() {

  HighScore = Score;
  document.getElementById("Highscore").innerHTML = HighScore;

}

//Snake Summon Stats
function SummonSnake() {

  snake.x = 200;
  snake.y = 200;
  snake.cells = [];
  snake.maxCells = 4;
  snake.dx = grid;
  snake.dy = 0;

}

//Gamemodes
function GameMode() {
  value = document.getElementById('valueGames').value;

  if (value === "E") {
    Speed = 15;
    Score = 0;
    document.getElementById("Score").innerHTML = Score;
    SummonSnake();
  } else if (value === "M") {
    Speed = 10;
    Score = 0;
    document.getElementById("Score").innerHTML = Score;
    SummonSnake();
  } else if (value === "H") {
    Speed = 5;
    Score = 0;
    document.getElementById("Score").innerHTML = Score;
    SummonSnake();
  } else if (value === "Multiplayer") {

  }
}
body {
  display: flex;
  align-items: center;
  justify-content: center;
  background-image: url('https://wallpapercave.com/wp/wp3493594.png');
}

canvas {
  width: 403px;
  height: 403px;
  border: 2px solid rgb(255, 213, 0);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

#ScoreCSS {
  margin-right: 50px;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <link rel="stylesheet" href="main.css">
  <meta charset="UTF-8">
  <title>Snake NICEEE</title>
</head>

<body>
  <!-- Scores -->
  <h1 id="ScoreCSS">Score: <a id="Score">0</a></h1>
  <h1>Highscore: <a id="Highscore">0</a></h1>

  <!-- Canvas (Game Field)-->
  <canvas id="game" width="400" height="400"></canvas>

  <!-- Dropdown for GameModes -->
  <select id="valueGames" onchange="GameMode()">
    <option value="E" selected>Easy</option>
    <option value="M">Middle</option>
    <option value="H">Hard</option>
    <option value="Multiplayer">Multiplayer</option>
  </select>
</body>

</html>


Solution

  • This should get you started. I created a second snake object and moved the alive property to the snake. Then I added functions to draw/move the snake and passed the snake object to those functions so you don't have as much duplicate code in order to handle multiple snakes. (Theoretically, you could add more than 2 snakes this way by creating new snake objects and then adding moveSnake(snake3, canvas); etc).

    You'll need to add the event listeners for WASD as well as "dead" game logic in Multiplayer mode, because presumably the game will pause somehow and show the winner and the high score.

    let canvas = document.getElementById('game');
    let ctx = canvas.getContext('2d');
    
    let grid = 10;
    let count = 0;
    
    //Scores
    let Score = 0;
    let HighScore = 0;
    
    let Speed = 15;
    
    //Snake Base Stats
    let snake = {
      x: 200,
      y: 200,
      dx: grid,
      dy: 0,
      cells: [],
      maxCells: 4,
      alive: true, // moved the global var here
      active: true, // added this in order to track multiple snakes being active
      color: 'white' // moved this here to define it per-snake
    };
    
    let snake2 = {
      x: 200,
      y: 200,
      dx: grid,
      dy: 0,
      cells: [],
      maxCells: 4,
      alive: true,
      active: false,
      color: 'red'
    };
    
    // keep track of active snakes
    let snakes = [
      snake
    ];
    
    //First Apple Spawn
    let apple = {
      x: 320,
      y: 320,
      color: 'gold'
    };
    
    //Random Int for Apple Spawn
    function getRandomInt(min, max) {
      return Math.floor(Math.random() * (max - min)) + min;
    }
    
    function loop() {
      requestAnimationFrame(loop);
    
      //Tic speed
      if (++count < Speed) {
        return;
      }
    
      count = 0;
      ctx.clearRect(0, 0, canvas.width, canvas.height);
    
      moveSnakes(snakes, canvas);
    
      //Apple Color
      ctx.fillStyle = apple.color;
      ctx.fillRect(apple.x, apple.y, grid - 1, grid - 1);
    
      drawSnakes(snakes, ctx, apple);
    }
    
    
    //Arrow Key Movement
    document.addEventListener('keydown', function(e) {
    
      if (e.which === 37 && snake.dx === 0) {
        snake.dx = -grid;
        snake.dy = 0;
      } else if (e.which === 38 && snake.dy === 0) {
        snake.dy = -grid;
        snake.dx = 0;
      } else if (e.which === 39 && snake.dx === 0) {
        snake.dx = grid;
        snake.dy = 0;
      } else if (e.which === 40 && snake.dy === 0) {
        snake.dy = grid;
        snake.dx = 0;
      }
    });
    
    //TODO: add movement for snake2
    
    requestAnimationFrame(loop);
    
    function moveSnakes(snakes, canvas) {
      snakes.forEach(function(snake) {
        if (!snake.active) {
          return;
        }
    
        snake.x += snake.dx;
        snake.y += snake.dy;
    
        if (snake.x < 0) {
          snake.x = canvas.width - grid;
        } else if (snake.x >= canvas.width) {
          snake.x = 0;
        }
    
        if (snake.y < 0) {
          snake.y = canvas.height - grid;
        } else if (snake.y >= canvas.height) {
          snake.y = 0;
        }
    
        snake.cells.unshift({
          x: snake.x,
          y: snake.y
        });
    
        if (snake.cells.length > snake.maxCells) {
          snake.cells.pop();
        }
      });
    }
    
    function drawSnakes(snakes, ctx, apple) {
      snakes.forEach(function(snake) {
        if (!snake.active) {
          return;
        }
    
        //Snake Color
        ctx.fillStyle = snake.color;
    
        snake.cells.forEach(function(cell, index) {
          //Snake Color
          ctx.fillRect(cell.x, cell.y, grid - 1, grid - 1);
    
          //Snake Eat Apple
          if (cell.x === apple.x && cell.y === apple.y) {
            snake.maxCells++;
            apple.x = getRandomInt(0, 25) * grid;
            apple.y = getRandomInt(0, 25) * grid;
            SnakeScore(snake);
          }
    
          //Snake Dies
          for (var i = index + 1; i < snake.cells.length; i++) {
    
            if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) {
    
              SummonSnake(snake);
              snake.alive = false;
              SnakeScore();
            }
          }
        });
      });
    }
    
    //Score
    function SnakeScore(snake) {
    
      if (snake.alive) {
        Score++;
        document.getElementById("Score").innerHTML = Score;
      } else if (!snake.alive) {
        Score = 0;
        document.getElementById("Score").innerHTML = Score;
        snake.alive = true;
      }
      if (Score > HighScore) {
        SnakeHighscore();
      }
    }
    
    //Highscore
    function SnakeHighscore() {
    
      HighScore = Score;
      document.getElementById("Highscore").innerHTML = HighScore;
    
    }
    
    //Snake Summon Stats
    function SummonSnake(snake, y) {
    
      snake.x = 200;
      snake.y = y;
      snake.cells = [];
      snake.maxCells = 4;
      snake.dx = grid;
      snake.dy = 0;
    
    }
    
    function activateSnakes(numberOfSnakes) {
      if (numberOfSnakes === 1) {
        SummonSnake(snake, 200);
        snakes = [snake];
        snake2.active = false;
      } else if (numberOfSnakes === 2) {
        SummonSnake(snake, 180);
        SummonSnake(snake2, 220);
        snakes = [snake, snake2];
        snake2.active = true;
      }
    }
    
    //Gamemodes
    function GameMode() {
      value = document.getElementById('valueGames').value;
    
      if (value === "E") {
        Speed = 15;
        activateSnakes(1);
      } else if (value === "M") {
        Speed = 10;
        activateSnakes(1);
      } else if (value === "H") {
        Speed = 5;
        activateSnakes(1);
      } else if (value === "Multiplayer") {
        Speed = 5;
        activateSnakes(2);
      }
      
      Score = 0;
      document.getElementById("Score").innerHTML = Score;
    }
    
    GameMode();
    body {
      display: flex;
      align-items: center;
      justify-content: center;
      background-image: url('https://wallpapercave.com/wp/wp3493594.png');
    }
    
    canvas {
      width: 403px;
      height: 403px;
      border: 2px solid rgb(255, 213, 0);
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
    
    #ScoreCSS {
      margin-right: 50px;
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <link rel="stylesheet" href="main.css">
      <meta charset="UTF-8">
      <title>Snake NICEEE</title>
    </head>
    
    <body>
      <!-- Scores -->
      <h1 id="ScoreCSS">Score: <a id="Score">0</a></h1>
      <h1>Highscore: <a id="Highscore">0</a></h1>
    
      <!-- Canvas (Game Field)-->
      <canvas id="game" width="400" height="400"></canvas>
    
      <!-- Dropdown for GameModes -->
      <select id="valueGames" onchange="GameMode()">
        <option value="E">Easy</option>
        <option value="M">Middle</option>
        <option value="H">Hard</option>
        <option value="Multiplayer" selected>Multiplayer</option>
      </select>
    </body>
    
    </html>