Jquery/JavaScript Collision Detection

jQuery/JavaScript collision detection

var overlaps = (function () {    function getPositions( elem ) {        var pos, width, height;        pos = $( elem ).position();        width = $( elem ).width();        height = $( elem ).height();        return [ [ pos.left, pos.left + width ], [ pos.top, pos.top + height ] ];    }
function comparePositions( p1, p2 ) { var r1, r2; r1 = p1[0] < p2[0] ? p1 : p2; r2 = p1[0] < p2[0] ? p2 : p1; return r1[1] > r2[0] || r1[0] === r2[0]; }
return function ( a, b ) { var pos1 = getPositions( a ), pos2 = getPositions( b ); return comparePositions( pos1[0], pos2[0] ) && comparePositions( pos1[1], pos2[1] ); };})();
$(function () { var area = $( '#area' )[0], box = $( '#box0' )[0], html; html = $( area ).children().not( box ).map( function ( i ) { return '<p>Red box + Box ' + ( i + 1 ) + ' = ' + overlaps( box, this ) + '</p>'; }).get().join( '' );
$( 'body' ).append( html );});
body {    padding: 30px;    color: #444;    font-family: Arial, sans-serif;}
h1 { font-size: 24px; margin-bottom: 20px;}
#area { border: 2px solid gray; width: 500px; height: 400px; position: relative;}
#area > div { background-color: rgba(122, 122, 122, 0.3); position: absolute; text-align: center; font-size: 50px; width: 60px; height: 60px;}
#box0 { background-color: rgba(255, 0, 0, 0.5) !important; top: 150px; left: 150px;}
#box1 { top: 260px; left: 50px;}
#box2 { top: 110px; left: 160px;}
#box3 { top: 200px; left: 200px;}
#box4 { top: 50px; left: 400px;}
p { margin: 5px 0;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script><h1>Detect overlapping with JavaScript</h1><div id="area">    <div id="box0"></div>    <div id="box1">1</div>    <div id="box2">2</div>    <div id="box3">3</div>    <div id="box4">4</div></div>

Collision Checking With JavaScript

I managed to find the following solution, thanks to stwitz' about idea, as well as this script: https://magently.com/blog/detecting-a-jquery-collision-part-iv/

var position = -1;
var $char = $('#char');
var keyCode = null;
var fired = false;
var stepSize = 32;
var $stones = $('.stones div');

//new
var cancelTop = cancelRight = cancelLeft = cancelBottom = false;

var charEl = $char[0].getBoundingClientRect();
var charLeft = parseInt(charEl.left);
var charRight = parseInt(charEl.right);
var charTop = parseInt(charEl.top);
var charBottom = parseInt(charEl.bottom);

function walking() {

if (position == 0 && !cancelTop) {
//if moving up & is safe to move up
} else if (position == 1 && !cancelBottom) {
//if moving down & is safe to move down
} else if (position == 2 && !cancelLeft) {
//if moving left and is safe to move left
} else if (position == 3 && !cancelRight) {
//if moving right and is safe to move right
}

cancelTop = cancelRight = cancelLeft = cancelBottom = false; //mark all as safe until we check

$stones.each(function() {

collision($(this));

});

}

document.onkeydown = function(e) {

keyCode = e.which || e.keyCode;

if (!fired) {
position = -1;
fired = true;
switch (keyCode) {
case 38: position = 0; break; //up
case 40: position = 1; break; //down
case 37: position = 2; break; //left
case 39: position = 3; break; //right
}

walking();
stepping = setInterval(walking,125);
}

};

document.onkeyup = function(e) {
//standing
clearInterval(stepping);
stepping = 0;
fired = false;
};


function collision($el) {

var el = $el[0].getBoundingClientRect();

var elBottom = parseInt(el.bottom);
var elRight = parseInt(el.right);
var elLeft = parseInt(el.left);
var elTop = parseInt(el.top);

if (
(elRight == charLeft) &&
(elBottom - stepSize >= charBottom && charBottom >= elTop + stepSize)
) {
cancelLeft = true;
return true;
}

if (
(elLeft == charRight) &&
(elBottom - stepSize >= charBottom && charBottom >= elTop + stepSize)
) {
cancelRight = true;
return true;
}

if (
(elTop + stepSize > charBottom) &&
(elTop <= charBottom) &&
(elLeft < charRight) &&
(elRight > charLeft)
)
{
cancelBottom = true;
return true;
}

if (
(elBottom - stepSize < charTop) &&
(elBottom >= charTop) &&
(elLeft < charRight) &&
(elRight > charLeft)
)
{
cancelTop = true;
return true;
}

return false;
}

Simple Javascript collision detection?

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>

collision detection on more than 2 divs with jQuery draggable?

  1. Firstly instead of using id to bind events use class selector.
  2. Use a flag to check if user is currently dragging any of the draggable item.
  3. Loop through all draggable items and test collision with respect to the current dragged element.

function collision(draggableItems) {  return draggableItems     .toArray()    .reduce(function(isColliding, currNode){        if(isColliding){return isColliding;}        var $currNode = $(currNode);        var x1 = $currNode.offset().left;        var y1 = $currNode.offset().top;        var h1 = $currNode.outerHeight(true);        var w1 = $currNode.outerWidth(true);        var b1 = y1 + h1;        var r1 = x1 + w1;        draggableItems.each(function(index, node){          if(node == currNode || isColliding){            return;          }          var $node = $(node);          var x2 = $node.offset().left;          var y2 = $node.offset().top;          var h2 = $node.outerHeight(true);          var w2 = $node.outerWidth(true);          var b2 = y2 + h1;          var r2 = x2 + w1;                          if(!(b1 < y2 || y1 > b2 || r1 < x2 || x1 > r2)){            isColliding = true;          }        });        return isColliding;  }, false);}

var isDragging = false;
$('.draggableItem') .on('mousedown', function(e){ isDragging = true; }) .on('mousemove',function(e) { if(!isDragging){return;} $('#result').text(collision($('.draggableItem'))); }) .on('mouseup', function(e){ isDragging = false; });
$('.draggableItem').draggable();
#div1 {  width: 200px;  height: 50px;  background-color: pink;}#div2 {  width: 200px;  height: 50px;  background-color: green;}#div3 {  width: 200px;  height: 50px;  background-color: red;}
<strong>Drag divs around.</strong>
<div id="div1" class="draggableItem"> Div1</div><br/><div id="div2" class="draggableItem"> Div2</div><br><div id="div3" class="draggableItem"> Div3</div>
<p>Colliding? <span id="result">false</span>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://code.jquery.com/ui/1.12.0/jquery-ui.js"></script>

Collision detection using pure jQuery is not giving desired output

As freefomn-m already said.
Check for collision in the animation cycle of the ship, not the walls.

For this I use the second type of parameters for jQuery's .animate method

.animate( properties, options )

I use the "progress" option to check the collision in every movement cycle of the ship.

console.clear();
$('document').ready(function() {

var collided = false;
var collidedWith = null;
var $ship = $('.ship');
var $walls = $('.wall')

var $totalHeight = $('.inner').height(); //of walls
var $maxHeight = Math.ceil(Math.ceil($totalHeight / 3) - (Math.ceil($totalHeight / 3) * 30) / 100); //30% of total wall height

$('.wall').each(function(i, obj) {
$(this).height($maxHeight);
$('.wall.four').css({
'height': $wallGap
});
})

var $wallGap = Math.ceil($totalHeight / 3) - $maxHeight;
var $wallOneTop = 0;
var $wallTwoTop = $maxHeight + $wallGap;
var $wallThreeTop = ($maxHeight * 2) + ($wallGap * 2);
var $wallFourTop = -$('.wall.four').height() - $wallGap;

$('.wall.one').css({
'top': $wallOneTop
});
$('.wall.two').css({
'top': $wallTwoTop
});
$('.wall.three').css({
'top': $wallThreeTop
});
$('.wall.four').css({
'top': $wallFourTop
});


function moveWall(wallObj) {
var $currentTop = wallObj.position().top;
var $limitTop = $('.inner').height();

if ($currentTop >= $limitTop) {
var $rand = Math.floor(Math.random() * ($maxHeight - $wallGap + 1) + $wallGap);
wallObj.height($rand);
var $top = -(wallObj.height());
} else {
var $top = (wallObj.position().top) + 5;
}
// var $collide = checkCollision(wallObj);
wallObj.css({
'top': $top
});
// return $collide;
}

var $wallTimer = setInterval(function() {
$walls.each(function(i, obj) {
moveWall($(this));
if (collided) {
clearInterval($wallTimer);
}
})
}, 40);


function checkCollision() {
var $shipWidth = $ship.width();
var $shipHeight = $ship.height();
var $shipLeft = $ship.position().left;
var $shipRight = $shipLeft + $shipWidth;
var $shipTop = $ship.position().top;
var $shipBottom = $shipTop + $shipHeight;

$('.wall').each(function(i) {

var $wall = $(this);
var $wallWidth = $wall.width();
var $wallHeight = $wall.height();
var $wallLeft = $wall.position().left;
var $wallRight = $wallLeft + $wallWidth;
var $wallTop = $wall.position().top;
var $wallBottom = $wallTop + $wallHeight;

if (
$shipLeft < $wallRight &&
$shipRight > $wallLeft &&
$shipTop < $wallBottom &&
$shipBottom > $wallTop
) {
console.log("dhumm!");
collided = true;
collidedWith = $wall
$wall.addClass('crashed')
$ship.addClass('crashed')
$ship.stop();
return false;
}
})
}







$('.outer .inner').click(function() {
var $ship;
$ship = $('.ship');
$shipLeft = $ship.position().left;
$shipRight = $shipLeft + $ship.width();

$inner = $('.inner');
$innerLeft = $inner.position().left;
$innerRight = $innerLeft + $inner.width();


if (($shipLeft < $inner.width() - $ship.width())) {
$ship.animate({
"left": $inner.width() - $ship.width()
}, {
"duration": 500,
"easing": "linear",
"progress": checkCollision,
});
} else if (($shipRight >= $inner.width())) {
$ship.animate({
"left": '0'
}, {
"duration": 500,
"easing": "linear",
"progress": checkCollision,
});
}

});


});
.outer {
background: #fff;
border: 20px solid #efefef;
width: 400px;
height: 600px;
display: inline-block;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
overflow: hidden;
}

.outer .inner {
background: #fff;
height: 100%;
width: 100%;
margin: auto;
position: relative;
overflow: hidden;
}

.outer .inner .wall {
width: 5px;
position: absolute;
left: 50%;
transform: translateX(-50%);
background: #000;
}
.outer .inner .wall.crashed {
background: red;
}


.outer .inner .ship {
width: 15px;
height: 15px;
background: orange;
position: absolute;
top: 50%;
transform: translateY(-50%);
}

.outer .inner .ship.crashed {
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer">
<div class="inner">
<div class="wall one"></div>
<div class="wall two"></div>
<div class="wall three"></div>
<div class="wall four"></div>

<div class="ship"></div>
</div>
</div>

Javascript collision detection system don't ignore blocked collisions

You have to treat horizontal and vertical collisions independently.

I've made some minor changes to your JS-fiddle: http://jsfiddle.net/Kf6cv/1/
it should work now, what I did is I split up your one check into two:

if (nextposy + height(circle) > rect.y &&
circle.x + width(circle) > rect.x &&
circle.x < rect.x + rect.width &&
nextposy < rect.y + rect.height) {
if (circle.y + height(circle) < rect.y) {
cls("top");
}
if (circle.y > rect.y + rect.height) {
cls("bottom");
}
}

if (nextposx + width(circle) > rect.x &&
nextposx < rect.x + rect.width &&
circle.y + height(circle) > rect.y &&
circle.y < rect.y + rect.height) {
if (circle.x + width(circle) < rect.x) {
cls("left");
}
if (circle.x > rect.x + rect.width) {
cls("right");
}
}

The reason for this is that if check both directions at once, it will prevent the movement(or make it bounce) for both directions(like the red figure in your picture) - even if it COULD move into one direction. The order of checking horizontal/vertical usually doesn't matter, it usually only matters if your "hero" approaches the other object 100% edge-to-edge. But what you could to is to first check the direction with the higher velocity, so if |velX| > |velY| then you first check for a horizontal collision.

Also I'd say it is safer to apply the new position directly after checking, right now it does two independend checks and then applies the movement of both directions - I'm not to sure about it but I could imagine that this might lead to some issues later.

JavaScript: Collision detection

The first thing to have is the actual function that will detect whether you have a collision between the ball and the object.

For the sake of performance it will be great to implement some crude collision detecting technique, e.g., bounding rectangles, and a more accurate one if needed in case you have collision detected, so that your function will run a little bit quicker but using exactly the same loop.

Another option that can help to increase performance is to do some pre-processing with the objects you have. For example you can break the whole area into cells like a generic table and store the appropriate object that are contained within the particular cells. Therefore to detect the collision you are detecting the cells occupied by the ball, get the objects from those cells and use your collision-detecting function.

To speed it up even more you can implement 2d-tree, quadtree or R-tree.

Collision detection with JavaScript. (Check if 2 Block Level elements are touching)

You can use the Jquery collision plugin like this:

var x = $("#div1").collision(".div2");


Related Topics



Leave a reply



Submit