5

I'm trying to make a simple game using jquery,javascript,html and css. I keep getting stuck on collision detection.

code:

var map = [
[0,1,0,0,0,1,0,0,],
[0,1,0,0,0,1,0,0,],
[0,1,0,0,0,1,0,0,],
[0,1,1,1,0,1,0,0,],
[0,0,0,0,0,0,0,2,]  
];

function DrawMap() {

    for(var i=0; i < map.length; i++){
        for(var j=0; j < map[i].length; j++){
            if(parseInt(map[i][j]) == 0){
            $("#container").append("<div class='air'></div>");
            }
            if(parseInt(map[i][j]) == 1){
            $("#container").append("<div class='block'></div>");
            }
            if(parseInt(map[i][j]) == 2){
            $("#container").append("<div class='spike'></div>");
            }
        }
    }

}

window.onload=function() {
    DrawMap();

}

more code:

var guy=document.getElementById('guy');

var up = 0;
var guyLeft = 0;
var health = 100;



function anim(e){



if(e.keyCode==83){
   up +=10;
   guy.style.top = up + 'px';
   if(up >=400){
     up -=10;
   }

 }

if(e.keyCode==87){
   up -=10;
   guy.style.top = up + 'px';
if(up <=0){
     up +=10;
   }  
}




 if(e.keyCode==68){
   guyLeft +=10;
   guy.style.left = guyLeft + 'px';
     if(guyLeft >= 700){
     guyLeft -= 10;
   }d
 }

if(e.keyCode==65){
   guyLeft -=10;
   guy.style.left = guyLeft + 'px';
 if(guyLeft <= 0){
     guyLeft += 10;
   }
}

}

document.onkeydown=anim;im;
5
  • document.onkeydown=anim;im; should be document.onkeydown=anim; I think? Also can you please add your html code here? Commented Dec 23, 2016 at 17:21
  • Fundamentally, the approach you likely will want to take is to keep track of where the guy is not just in pixels, but in map[i][j] coordinates. Then you can check the next cell that the guy will enter to determine if he should collide or not. In fact it would probably be better to only track guy's location in i, j coordinates and then use a function to map those to the display coordinates. Commented Dec 23, 2016 at 17:32
  • you could test if (up <= 0) { up = 0; } first and then assign style. Commented Dec 23, 2016 at 18:02
  • sidenote: In your if-clauses, use this instead: if(e.keyCode==83){up+=10; if(up>400){up-=10;} guy.style.top=up+'px';} (click for link). This is what Nina Scholz meant in the comment above. Commented Dec 23, 2016 at 22:07
  • just a tip: If you're already using jQuery anyway, go all in. It will make your life so much easier. So use the jQuery equivalence of all your plain JS, such as $(window).load(function(){...}); instead of window.onload = function(){...};, and $(document).keydown(function(){...}); instead of document.onkeydown=function(){...};, etc... Commented Dec 23, 2016 at 22:16

1 Answer 1

5

Short answer: use document.elementFromPoint(x,y); to detect which element is at the position of the player.

Long answer: Have a look at the code snippet below.

Now, I realize that I changed a whole lot in your code, including unnecessary changes for functionality. My apologies, this is because initially I was just playing around for myself, but now that it's working, it might be useful for somebody.
(Some of the syntax used may be unknown to you, see the bottom of my post for some explanations.)

The collision part is in function checkCollision() and the if-clauses inside $(document).keydown:

  • In the keydown if-clauses checkCollision() is invoked: else {tile = checkCollision(...);.
  • Basically I check if the front of the player (topview) is going to collide at the coming position:
    (for (var i=corner1; i<corner2; ++i) {...}).
    • Based on the colliding tile ('spike' or 'block'), I either generate an alert (console.log), or I return the checkTile to the if-clause that invoked checkCollision().
  • If tile is returned, the tile's position is used to determine the position of the player:
    $(player).css("left",(tile?$(tile).offset().left+$(tile).width():x));.

Code snippet:

//DOCUMENT-READY==========================
$(document).ready(function() {
  //VARS----------------------------------
  var step = 10;
  var health = 100;
  
  //MAP-----------------------------------
  var map = [
    [0,1,0,0,0,1,0,0],
    [0,1,0,0,0,1,0,0],
    [0,1,0,0,0,1,0,0],
    [0,1,1,1,0,1,0,0],
    [0,0,0,0,0,0,0,2]
  ];
  
  //DRAW MAP------------------------------
  (function() {
    var tile = "";
    for (var y=0,county=map.length; y<county; ++y) {
      $("#canvas").append('<div class="tile-row" id="row_'+(y+1)+'"></div>');
      for (var x=0,countx=map[y].length; x<countx; ++x) {
        switch (parseInt(map[y][x])) {
          case 0: tile="air"; break;
          case 1: tile="block"; break;
          case 2: tile="spike"; break;
          default: tile="error";
        }
        $("#row_"+(y+1)).append('<div class="tile '+tile+'"></div>');
      }
    }
  })();
  
  //SET BOUNDARIES------------------------
  var xMin=$("#canvas").offset().left, xMax=xMin+$("#canvas").width()-$("#player").width();
  var yMin=$("#canvas").offset().top, yMax=yMin+$("#canvas").height()-$("#player").height();
  
  //PLACE PLAYER--------------------------
  $("#player").css("left",xMin).css("top",yMin);
  
  //MOVE PLAYER---------------------------
  $(document).keydown(function(e){
    var player=document.getElementById("player"), tile=null;
    var x=$(player).offset().left, playerLeft=x, playerRight=playerLeft+$(player).width();
    var y=$(player).offset().top, playerTop=y, playerBottom=playerTop+$(player).height();
    
    function checkCollision(playerCorner1x, playerCorner1y, playerCorner2x, playerCorner2y) {
      var collisionTiles=["block","spike"];
      //check if the front of the player is colliding with the environment
      var front = (playerCorner1x==playerCorner2x?playerCorner1x:playerCorner1y);
      var corner1 = (front==playerCorner1x?playerCorner1y:playerCorner1x);
      var corner2 = (front==playerCorner1x?playerCorner2y:playerCorner2x);
      //check every pixel along the front for collision
      for (var i=corner1; i<corner2; ++i) {
        var checkTile = document.elementFromPoint((front==playerCorner1x?front:i), (front==playerCorner1y?front:i));
        if (collisionTiles.indexOf(checkTile.className.split(" ")[1]) != -1) {
          if ($(checkTile).hasClass("spike")) {console.log("YOU DEAD!");}
          else if ($(checkTile).hasClass("block")) {return checkTile;}
          break;
        }
      }
    }
    
    if(e.which==37 || e.which==65){ //LEFT,A
      x -= step;
      if (x <= xMin) {x = xMin;}
      else {tile = checkCollision(playerLeft-step,playerTop, playerLeft-step,playerBottom);}
      $(player).css("left",(tile?$(tile).offset().left+$(tile).width():x));
    }
    if(e.which==39 || e.which==68){ //RIGHT,D
      x += step;
      if (x >= xMax) {x = xMax;}
      else {tile = checkCollision(playerRight+step,playerTop, playerRight+step,playerBottom);}
      $(player).css("left",(tile?$(tile).offset().left-$(player).width():x));
    }
    if(e.which==38 || e.which==87){ //UP,W
      y -= step;
      if (y <= yMin) {y = yMin;}
      else {tile = checkCollision(playerLeft,playerTop-step, playerRight,playerTop-step);}
      $(player).css("top",(tile?$(tile).offset().top+$(tile).height():y));
    }
    if(e.which==40 || e.which==83){ //DOWN,S
      y += step;
      if (y >= yMax) {y = yMax;}
      else {tile = checkCollision(playerLeft,playerBottom+step, playerRight,playerBottom+step);}
      $(player).css("top",(tile?$(tile).offset().top-$(player).height():y));
    }
  });
});
div {margin:0; padding:0; border-style:none; box-sizing:border-box; overflow:hidden;}

/*CANVAS================*/
#canvas {display:inline-block;}

/*TILES=================*/
.tile-row {width:100%; height:40px;}
.tile {float:left; width:40px; height:100%;}
.tile.air {background-color:skyblue;}
.tile.block {background-color:sienna;}
.tile.spike {background-color:gray;}
.tile.error {background-color:red;}

/*PLAYER================*/
#player {position:absolute; width:15px; height:15px; background-color:black;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id="canvas"></div>
<div id="player"></div>
jsfiddle: https://jsfiddle.net/Lqw9w06z/11/

Explanations/Comments:

  • (function(){...})(); is a IIFE (Immediately Invoked Function Expression), it's a function that is automatically executed.
  • front==playerCorner1x?playerCorner1y:playerCorner1x is called a Conditional (ternary) Operator, it's basically a compact if-clause.
  • The size of the tiles is set in CSS:
    .tile-row {width:100%; height:40px;} and .tile {width:40px; height:100%;}.
    This determines the size of the whole canvas/gameboard.
  • I changed your for-loop so that all the tiles of one row are wrapped in a containing div.

SIDENOTE:

For some reason, collision isn't registered at one specific spot (see below).
If someone can explain to me why this is happening in this one specific spot, I'd be delighted!

collision-bug

Sign up to request clarification or add additional context in comments.

1 Comment

This helped me overcome my biggest road block, thank you (even though the website "suggests" that I don't.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.