Search code examples
javascriptcollision-detectionkineticjscollision

Dragging collisions


I am pretty new to both CANVAS and Kineticjs but I feel what I am attemping should be much easier then I am making it out to be. Basically this is what I have so far:

The code I am attempting to use is from kineticjs Stop drag to a shape when overlaps with another solution but was unable to get it to work.

Please check the live jsfiddle code

var isRectCollide = function(target, box) {
  if (target.x - target.width  >= box.x + box.width  &&
  target.y - target.height >= box.y + box.height &&
  target.x + target.width  <= box.x + box.width  &&
  target.x + target.height <= box.y - box.height )
    return false;
  else
    return true;
}

The idea of this is for the pink square to be draggable but blocked by the orange box, once dragged around the orange box and the pink box 'touches' the blue box and pop up should occur.

I am not sure if using kineticjs is the simplest way to implement this though?

Any ideas, tips or help would me much appreciated.


Solution

  • Yes, since KineticJS doesn't have collision testing you must do your own.

    Here is a collision test between any 2 kineticJS rectangles:

    function theyAreColliding(rect1, rect2) {
      return !(rect2.getX() > rect1.getX()+rect1.getWidth() || 
               rect2.getX()+rect2.getWidth() < rect1.getX() || 
               rect2.getY() > rect1.getY()+rect1.getHeight() ||
               rect2.getY()+rect2.getHeight() < rect1.getY());
    }
    

    And here is how you would call the collision test between the box and obstacle:

    if( theyAreColliding(box,obstacle){
          // obstacle is blocking box
          alert("You are being blocked!");
    }
    

    And here is how you would call the collision test between the box and the goal:

    if( theyAreColliding(box,target){
          // box touched the goal
          alert("Goal!");
    }
    

    To stop the box from dragging right through the obstacle, you must give box a custom drag function like this:

    dragBoundFunc: function(pos){
        if(theyAreColliding(box,obstacle){
            // box is touching obstacle
            // don't let box move down
            return({ x:pos.x, y:obstacle.getY()-1 });
        } else{
            // box is not touching obstacle
            // let it move ahead
            return({ x:pos.x, y:pos.y });
        }
    }
    

    You can see how this works in a demo at: http://www.html5canvastutorials.com/kineticjs/html5-canvas-drag-and-drop-bounds-tutorial-with-kineticjs/

    [Edit: specify where each code goes]

    I put the pieces together into a working snippet below. I did find one unfortunate thing. It’s possible for the user to drag the pink box fast enough to go right through the obstacle—KineticJS can’t react fast enough to stop a very fast drag.

    Also--oops on me. I corrected some missing parentheses in the theyAreColliding function above.

    The dragBoundFunc goes as an addition to the box constructor (see code below).

    You can test if the user has a goal by testing in the box’s “dragmove” handler, like this:

      box.on('dragmove', function() {
        if (theyAreColliding(box, target)) {
            // box touched the goal
            alert("Goal!");
        }      
      });
    

    Here is code and a Fiddle: http://jsfiddle.net/uCAys/

    <!DOCTYPE HTML>
    <html>
      <head>
        <style>
    body {
        margin: 0px;
        padding: 20px;
    }
    canvas {
        border: 1px solid #777;
    }
        </style>
      </head>
      <body>
        <div id="container"></div>
        <script src="http://www.html5canvastutorials.com/libraries/kinetic-v4.3.1-beta2.js"></script>
        <script>
            var stage = new Kinetic.Stage({
                container: 'container',
                width: 300,
                height: 300
            });
            var layer = new Kinetic.Layer();
    
            //Dragable Pink box
            var box = new Kinetic.Rect({
                x: 100,
                y: 50,
                width: 100,
                height: 50,
                fill: 'pink',
                stroke: 'black',
                strokeWidth: 2,
                draggable: true,
                // this causes box to be stopped if contacting obstacle
                dragBoundFunc: function(pos){
                    if(theyAreColliding(box,obstacle)){
                        // box is touching obstacle
                        // don't let box move down
                        return({ 
                            x: pos.x, 
                            y: Math.min( obstacle.getY()-box.getHeight()-1, pos.y)
                        });
                    } else{
                        // box is not touching obstacle
                        // let it move ahead
                        return({ x:pos.x, y:pos.y });
                    }
                } 
            });
    
          box.on('dragmove', function() {
            if (theyAreColliding(box, target)) {
                // box touched the goal
                box.setX(100);
                box.setY(50);
                alert("Goal!");
            }      
          });
    
            // End goal blue box
            var target = new Kinetic.Rect({
                x: 100,
                y: 200,
                width: 100,
                height: 50,
                fill: 'blue',
                stroke: 'black',
                strokeWidth: 2
            });
    
            // Obstacle/blocker orange box
            var obstacle = new Kinetic.Rect({
                x: 125,
                y: 145,
                width: 50,
                height: 30,
                fill: 'orange',
                stroke: 'black',
                strokeWidth: 2
            });
    
            function theyAreColliding(rect1, rect2) {
                return !(rect2.getX() > rect1.getX() + rect1.getWidth() ||  //
                         rect2.getX() + rect2.getWidth() < rect1.getX() ||  // 
                         rect2.getY() > rect1.getY() + rect1.getHeight() ||   //
                         rect2.getY() + rect2.getHeight() < rect1.getY());  //
            }
    
            layer.add(box);
            layer.add(obstacle);
            layer.add(target);
            stage.add(layer);
    
        </script>
      </body>
    </html>