Search code examples
javascriptraphaelcollision-detectioncollision

How can I improve on this JavaScript Collision Detection?


I have a test application where I am creating collison detection between to rectangles using Raphael.js.

I am able to get collison detection working properly, but I have to drag it around slooowly.... the issue arises when I move the mouse too quickly. It seems that it is not refreshing quick enough to detect the draggable rect.

The purple square is the only one that drags.

JS Fiddle

I guess my question is how can I improve detection/fix my issue?

Thanks in advance.


Solution

  • Since move is getting called on each pixel move, you don't have time to do much in the way of calculations to keep it smooth. First, I replaced you function for determining overlap with a more standard one:

    var rect_collision = function (x1, y1, size1, x2, y2, size2) {
      var a = {top: y1, bottom: y1+size1, left: x1, right: x1+size1};
      var b = {top: y2, bottom: y2+size2, left: x2, right: x2+size2};
    
      // this is the general way to figure out if two rects are overlapping
      return !(a.left >= b.right || a.right <= b.left ||                           
               a.top >= b.bottom || a.bottom <= b.top);
    };
    

    This just checks to see if one rectangle is completely to the left, right, top, or bottom of the other one. If it isn't, then they must be overlapping. Since this just gives a true or false value, I still had to figure out which side the collision occurred on.

    To figure that out, I broke the collisions into two components, an x collision and a y collision by pretending that first only dx changed then only dy changed. Once I knew which direction was causing the overlap I could then use the change in direction to determine which side the overlap occurred on. For example, if x caused the collision and the previous dx was greater than the current dx then the collision was on the right side.

      // check the x and y directions separately                                        
      var x_collide = rect_collision(r2_x, r2_y, rectSize, x, r1_y, rectSize);
    
      // see if we are currently overlapping
      if (!x_collide) {
        // not colliding, update our x position normally
        this.attr({x:x});
        this.pdx = dx;                          
      }
      else {
        // we are, stick the moving rect to the correct side of the stationary one
        // based on the drag direction that got us stuck
        this.attr({x: this.pdx > dx ? r2_x + rectSize + 1 : r2_x - rectSize - 1});                       
      }
    

    I then added a little bit of extra logic to match the functionality that you had which prevent the user from dragging the rectangle directly through the stationary one. Basically I ended up just seeing if the move would place the moving rectangle directly on the opposite side of the stationary one, and if so, prevent it.

    I also cleaned up your checks for the border to get rid of all of the Math.min and Math.max calls since you didn't really need those. That's more of a preference thing though since I doubt there were causing much of the performance issues.

    You can see the results at http://jsfiddle.net/X7H9G/3/. I'm not sure if this is the best solution, but it seems to do the job.