Search code examples
javascripthtmlcsscollision-detection

Adding walls for Top-Down js, html, css game (Collision Detection)


I have a VERY small 2d top-down game. I don't really think I need to explain the whole game for this question, but you can play around with it in the code snippet, to get an idea. I want to make walls/obstacles, maybe just a few blocks, that the player can collide with, I can add the other walls later once I learn the code for how to do it. You can read the directions at the bottom of the screen in the snippet to see the controlls, if you need to.

Here is my code for the game in HTML, CSS, and JS:

//Canvas
mycan.style.display = "block";
mycan.height = 600;
mycan.width = 600;
//make players
let player = {x:510, y: 541, w:30, h:30};
let player2 = {x:60, y:31, w:30, h:30};

//Context
const ctx = mycan.getContext("2d");


const moveHandler = (isPlayer1) => {
    if (player.x == player2.x && player.y == player2.y) {
        document.getElementById('info').textContent = isPlayer1 ? 'Player1 killed player2' : 'Player2 killed player1'
    }
}

//Start-position
ctx.fillRect(player.x, player.y, player.w, player.h);
ctx.fillRect(player2.x, player2.y, player2.w, player2.h);
//No-smooth-movement
window.onkeydown = move = (e) => {
    let key = e.key;
  //player1(red)
    switch (key) {
        case 'w':
            player2.y -= 30;
            moveHandler(false);
            break;
        case 'a':
            player2.x -= 30;
            moveHandler(false);
            break;
        case 's':
            player2.y += 30;
            moveHandler(false);
            break;
        case 'd':
            player2.x += 30;
            moveHandler(false);
            break;
        case 'ArrowRight':
            player.x += 30;
            moveHandler(true);
            break;
        case 'ArrowLeft':
            player.x -= 30;
            moveHandler(true);
            break;
        case 'ArrowDown':
            player.y += 30;
            moveHandler(true);
            break;
        case 'ArrowUp':
            player.y -= 30;
            moveHandler(true);
            break;
    }
}

const draw = ()=>{
//player draw, and player colors
  ctx.clearRect(0,0, mycan.width, mycan.height);
  ctx.fillRect(player.x, player.y, player.w, player.h);
  ctx.fillStyle = "blue";
  ctx.fillRect(player2.x,player2.y, player2.w, player2.h);
  ctx.fillStyle = 'red';
  
  
  
};

setInterval(()=>{
  draw();
},1000/60);
html, body {
    margin: 20;
    padding: 10;
  }
  canvas {
    display: block;
  }
  
  #mycan {
    background-size: 30px 30px;
    background-image:
      linear-gradient(to right, black 1px, transparent 1px),
      linear-gradient(to bottom, black 1px, rgb(136, 136, 136) 1px);
  
  }


  body {
    background-color: grey;

  }
<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />
<canvas id = "mycan" > </canvas>
    <p id="info"></p>
    <font color = 'blue'> <h1>Player1 = blue</h1></font>
    <font color = 'red'> <h1>Player2 = red</h1></font>

  </head>
  <body>
    <main>
    </main>
    <script src="script.js"></script>
   
   <h2>
   Direction:
   Player1(Blue), is controlled by the WASD keys, and player2(red), is controlled by the arrow keys. The objecitve of the game is to stop in front
   of the other player, and let them run into you, if they run into you, then they die, at the same time you have avod running into the other
   player.Good luck, and have fun.
   </h2>
   
   

  </body>
</html>

Feel free to ask any question about the code, or the question, in the comments!


Solution

  • What you are trying to do is a really common thing found in games, i would recommend learning more about game development and objects. Here are some usefull articles:

    2d collision detection: https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection

    Tiles and tilemaps: https://developer.mozilla.org/en-US/docs/Games/Techniques/Tilemaps

    Objects in javascript: https://www.w3schools.com/js/js_objects.asp

    There is also a lot more information if you know what to look for.

    But to answer your question:

    You should consider using objects for the players and all the walls (entities) you are going to collide, but a simple example will be to create a wall:

    let obsticle = {x: 90, y: 91, w: 30, h:30};
    

    Render it on the page as you do with the players

    ctx.fillStyle = 'green';
    ctx.fillRect(obsticle.x, obsticle.y, obsticle.w, obsticle.h);
    

    Then create a function that checks for collision:

    function checkCollison(rect1, rect2) {
        if (
            rect1.x < rect2.x + rect2.w &&
            rect1.x + rect1.w > rect2.x &&
            rect1.y < rect2.y + rect2.h &&
            rect1.h + rect1.y > rect2.y
        ) {
            // Collision detected!
            return true;
        } else {
            // No collision
            return false;
        }
    }
    

    Also i recomend creating a function for the movement of the players because everytime a players move you will have to call checkCollision() and if there is a collision then the players cant move, or put him back in his original place, something like this:

    function movePlayer(object, x, y) {
        object.x += x;
        object.y += y;
    
        if (checkCollison(object, obsticle)) {
            object.x -= x;
            object.y -= y;
            return;
        };
    }
    

    So your movement becomes:

     case 'w':
            movePlayer(player2, 0, -30);
            moveHandler(false);
            break;
    

    If you use objects and tilemaping for everything this process will be way easier and your code will become more scalable

    Also here is a sniper so you can test it out

    //Canvas
    mycan.style.display = "block";
    mycan.height = 600;
    mycan.width = 600;
    //make players
    let player = {x:510, y: 541, w:30, h:30};
    let player2 = {x:60, y:31, w:30, h:30};
    
    let obsticle = {x: 90, y: 91, w: 30, h:30};
    
    //Context
    const ctx = mycan.getContext("2d");
    
    function checkCollison(rect1, rect2) {
        if (
            rect1.x < rect2.x + rect2.w &&
            rect1.x + rect1.w > rect2.x &&
            rect1.y < rect2.y + rect2.h &&
            rect1.h + rect1.y > rect2.y
        ) {
            // Collision detected!
            return true;
        } else {
            // No collision
            return false;
        }
    }
    
    function movePlayer(object, x, y) {
        object.x += x;
        object.y += y;
    
        if (checkCollison(object, obsticle)) {
            object.x -= x;
            object.y -= y;
            return;
        };
    }
    
    const moveHandler = (isPlayer1) => {
        if (player.x == player2.x && player.y == player2.y) {
            document.getElementById('info').textContent = isPlayer1 ? 'Player1 killed player2' : 'Player2 killed player1'
        }
    }
    
    //Start-position
    ctx.fillRect(player.x, player.y, player.w, player.h);
    ctx.fillRect(player2.x, player2.y, player2.w, player2.h);
    //No-smooth-movement
    window.onkeydown = move = (e) => {
        let key = e.key;
      //player1(red)
        switch (key) {
            case 'w':
                movePlayer(player2, 0, -30);
                moveHandler(false);
                break;
            case 'a':
                movePlayer(player2, -30, 0);
                moveHandler(false);
                break;
            case 's':
                movePlayer(player2, 0, 30);
                moveHandler(false);
                break;
            case 'd':
                movePlayer(player2, 30, 0);
                moveHandler(false);
                break;
            case 'ArrowRight':
                movePlayer(player, 30, 0);
                moveHandler(true);
                break;
            case 'ArrowLeft':
                movePlayer(player, -30, 0);
                moveHandler(true);
                break;
            case 'ArrowDown':
                movePlayer(player, 0, 30);
                moveHandler(true);
                break;
            case 'ArrowUp':
                movePlayer(player, 0, -30);
                moveHandler(true);
                break;
        }
    }
    
    const draw = ()=>{
    //player draw, and player colors
      ctx.clearRect(0,0, mycan.width, mycan.height);
      ctx.fillStyle = "blue";
      ctx.fillRect(player.x, player.y, player.w, player.h);
      ctx.fillStyle = 'red';
      ctx.fillRect(player2.x,player2.y, player2.w, player2.h);
      ctx.fillStyle = 'green';
      ctx.fillRect(obsticle.x, obsticle.y, obsticle.w, obsticle.h);
    
    };
    
    setInterval(()=>{
      draw();
    },1000/60);
    html, body {
        margin: 20;
        padding: 10;
      }
      canvas {
        display: block;
      }
      
      #mycan {
        background-size: 30px 30px;
        background-image:
          linear-gradient(to right, black 1px, transparent 1px),
          linear-gradient(to bottom, black 1px, rgb(136, 136, 136) 1px);
      
      }
    
    
      body {
        background-color: grey;
    
      }
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta charset="utf-8" />
    <canvas id = "mycan" > </canvas>
        <p id="info"></p>
        <font color = 'blue'> <h1>Player1 = blue</h1></font>
        <font color = 'red'> <h1>Player2 = red</h1></font>
    
      </head>
      <body>
        <main>
        </main>
        <script src="script.js"></script>
       
       <h2>
       Direction:
       Player1(Blue), is controlled by the WASD keys, and player2(red), is controlled by the arrow keys. The objecitve of the game is to stop in front
       of the other player, and let them run into you, if they run into you, then they die, at the same time you have avod running into the other
       player.Good luck, and have fun.
       </h2>
       
       
    
      </body>
    </html>