Collision Detection

At this point you should be familiar with functions, animations, and if statements. This tutorial introduces collision detection, which allows you to determine when two shapes touch. If you can determine that two shapes touch, you can trigger some action- think of detecting when the user has moused over a button, or when a game character touches the floor or a badguy, or when your animation reaches a certain state.


This is a companion discussion topic for the original entry at https://happycoding.io/tutorials/processing/collision-detection
2 Likes

In your tutorial on collision detection where a moving rectangle collides with a stationary center rectangle it appears to fail if the moving rectangle is moving at 1 pixel speed in both the x and y and collides exactly on the corner of the center stationary rectangle. This because on the next frame the x and y individually fail. Can you confirm this and suggest a fix for this.

This was precisely what i was looking for but am not sure if i have got something wrong.

Many thanks in advance.

Greg

Hey Greg, in what way does it fail for you?

Here’s what I see:

cd

Is that different from what you’re seeing?

A bit further checking and the problem is irrespective of the x and y speeds as long as there is an x and y speed both moving towards the center rectangle. The bouncing rectangle must be touching the corner but not overlapping the center rectangle. In this scenario it fails to detect the collision.

However, this may not be too much of a problem as the collision detection does work on the next frame after the bouncing rectangle has moved on this frame. Not ideal but may be okay.

A possible solution might be to ensure, for example, if you are moving at say 1 pixel per frame change it to 1.01 pixels per frame. This will look like it is butting up to center rectangle but will infact be probably overlapping by some smalll fraction of a pixel internally but not visibly.

I think it might depend on how we define a collision. If the bouncing rectangle is touching the corner but not overlapping, is that really a collision? What if the bouncing rectangle stopped, or started going straight down or straight right instead of going diagonally?

I personally think that’s it’s correct that touching but not overlapping is not counted as collision.

In fact, I think you could ask a similar question about collision on an edge, not on a corner. If the bouncing rectangle is touching but not overlapping the center rectangle, that won’t be counted as a collision no matter if it’s in the corner or not.

If you’re worried that the bouncing rectangle might overlap with the center rectangle for a frame, you can try snapping it to an edge when a collision is detected.

Does that make sense, or are you worried about something else?

I dont think i explained myself very clearly so hopefully this will be clearer. Im more curious than anything else.

1 Like

Hi Kevin.

I must first apologise as it seems i may of been a little arrogant and a touch pushy. Sorry for that.

If i may explain my drawing above.

As you can see the 2 objects do not overlap and therefore do not collide. So when the 2 algorithms are run you would expect them to detect that i will collide if i continue to move and therefore reverse the bouncingrect x & y. It does not do this until the next frame after they have collided. Despite what i said your algorithms do not fail, it is simply a little oddity on this precise situation. You are absolutely correct when you say that a check for this can be done and the bouncingrect can be repositioned.

Thank you for a great and simple algorithm :+1:

2 Likes

No worries, I didn’t take it that way.

But I totally get what you’re saying. This is interesting because I think there’s a difference in how we as humans think of collision, and what the code is telling the computer to do.

In our human brains, we know that the bouncing rectangle is moving towards the center rectangle, so we see the case you’re describing (where the rectangles are touching but not overlapping yet) as a collision. But I think that’s because our brains are pattern matching based on our expectations, which the computer isn’t smart enough to do.

To explain what I mean, imagine that we’re using this collision detection algorithm in a game where you control a car (with a rectangle bounding box) that’s driving around obstacles (also with rectangle bounding boxes). If somebody is really good at the game, they might be able to maneuver the car so it barely passes by an obstacle. Should this be a collision?

You might say that it depends on the direction of the car, but what if on the last frame I was moving to the upper right, but on the next frame I’m only going to be moving up?

Or think about it in reverse: what if this is for a game where I need to make sure one rectangle is touching another one, and I lose if they stop overlapping? Should the “touching but not quite overlapping” case count as overlapping?

Part of what makes this interesting to me is that there aren’t right or wrong answers to those questions. It depends on exactly what behavior you want!

Another way around this is to add a third if statement that checks what would happen if both x and y were updated. But whether you want that depends on your exact goal.

Hi everyone,
I am pleased to speak briefly in this beautiful forum after a long time !
I’m very interested in the subject of collisions (on another occasion I will send my own little algorithms).
My language is Italian, but I believe that in this case there is no difference with the English language: I would like to invite everyone to reflect on the difference between contact and collision.
I also ask myself: are the vertical sides of two different polygons having the same x (abscissa) intersected or overlapped (especially in the field of integers, like a “simple pixel area”)?
Bye and good reasoning to all ! :grinning: :wave:

1 Like

image 2
Hi Kevin, I found a typo here :point_up_2:

Thanks, fixed!

Hey, kevin

Thank you for making this collision page, I now know how to code The Jump and am able to understand 2D arrays a bit more now which is one of my biggest struggles. Just wanted to say thanks :slight_smile:

1 Like

hi dude, I am a very newbie in coding please I have some questions

q no 1:I want to Count how many times, collisions if both objects collide with each other
q no 2: how to count collision time
plz help me

At a high level, I think you’d want to create a variable that keeps track of how many times your objects have collided. I see in the chat that you have code like this:

 if (nextBouncingRight > centerRectX && 
      nextBouncingLeft < centerRectX + centerRectWidth && 
      currentBouncingBottom > centerRectY && 
      currentBouncingTop < centerRectY + centerRectHeight) {
    bouncingRectSpeedX *= -1;
  }

If at the top of your class you created a collisionCount variable:

int collisionCount = 0;

Then your code could increment that variable whenever it detected a collision:

 if (nextBouncingRight > centerRectX && 
      nextBouncingLeft < centerRectX + centerRectWidth && 
      currentBouncingBottom > centerRectY && 
      currentBouncingTop < centerRectY + centerRectHeight) {
    bouncingRectSpeedX *= -1;
    collisionCount++;
  }

Hope that helps!

Hello i have a question. I don’t understand this line:

boolean[][] grid = new boolean[rows][columns];

I haven’t seen a double [ ] before and don’t understand how it works of what it dose could you please explain.

OK i don’t understand how to fix it but [] means [.]. The . is so it don’t turn into the box. (Edit: fixed!)

The double [][] is creating a 2D array. It’s a common way of storing information in a grid or matrix.

More info here:

thank you very much and i would also like to thank you for the tutorials as i just finished the processing tutorials

2 Likes

hey i was woking on a game program but the collison detection is proving triky and have been tring to get it to work for a few days but still not sure how to fix it it seems to be a false dection of the right and left hits but is not very consitent and only some time cuases issuss

ArrayList rectangles = new ArrayList();
float PlayerX;
float PlayerY;
float PlayerW;
float PlayerSX;
float PlayerSY;
boolean upPressed = false;
boolean downPressed = false;
boolean leftPressed = false;
boolean rightPressed = false;
boolean jumping = false;
float GroundY;
float GroundX;
boolean jumpingg =false;

void setup(){
size(600,400);
background(255);
frameRate(60);
GroundY = height-20;
GroundX = 20;
PlayerW = 50;
PlayerX = 420;
PlayerY = 200;
PlayerSX = 0;
PlayerSY = 0;
addObj();
}

void draw(){
background(0);
PlayerMove();
Collision();
drawPlayer(PlayerX,PlayerY,PlayerW);
println(PlayerSY);
println(PlayerY);
println(jumping);
}

void Collision(){
for (int i = 0; i < rectangles.size(); i++) {
Rectangle rectangle = rectangles.get(i);
if (PlayerX + PlayerW + PlayerSX > rectangle.x &&
PlayerX + PlayerSX < rectangle.x + rectangle.rectWidth &&
PlayerY + PlayerW > rectangle.y &&
PlayerY < rectangle.y + rectangle.rectHeight) {
if(PlayerX <= rectangle.x){
PlayerX = rectangle.x - PlayerW;
}
if(PlayerX + PlayerW >= rectangle.x + rectangle.rectWidth){
PlayerX = rectangle.x + rectangle.rectWidth;

    }
    PlayerSX = 0;
  }
  
if (
  PlayerX + PlayerW > rectangle.x && 
  PlayerX < rectangle.x + rectangle.rectWidth && 
  PlayerY + PlayerW + PlayerSY > rectangle.y && 
  PlayerY + PlayerSY < rectangle.y + rectangle.rectHeight) {
    if(PlayerY <= rectangle.y){
      jumping = false;
      PlayerY = rectangle.y - PlayerW;
    }
    if(PlayerY >= rectangle.y + rectangle.rectHeight){
      PlayerY = rectangle.y + rectangle.rectHeight;
    }
    PlayerSY = 0;
  } 
fill(255, 0, 0);
rect(rectangle.x, rectangle.y, rectangle.rectWidth, rectangle.rectHeight);

}
}

void PlayerMove(){
if (!rightPressed || !leftPressed) {
PlayerSX = 0;
}
if (upPressed) {
if (!jumping) {
PlayerSY = -15;
jumping = true;
}
}
if (downPressed) {
}
if (leftPressed) {
PlayerSX -= 1;
}
if (rightPressed) {
PlayerSX = 1;
}
if (jumping) {
PlayerSY ++;
}

for (int i = 0; i < rectangles.size(); i++) {
Rectangle rectangle = rectangles.get(i);
if(!jumping && !(
PlayerX + PlayerW > rectangle.x &&
PlayerX < rectangle.x + rectangle.rectWidth &&
PlayerY + PlayerW + 1 > rectangle.y &&
PlayerY + 1< rectangle.y + rectangle.rectHeight)) {
jumping = true;
}
}

PlayerY += PlayerSY;
PlayerX += PlayerSX;

}

void drawPlayer(float playerX, float playerY, float playerW){
fill(0,255,0);
rect(playerX, playerY, playerW, playerW);
}

void addObj(){
/* rectangles.add(new Rectangle(0, 0, width, 20));
rectangles.add(new Rectangle(0, GroundY, width, 20));
rectangles.add(new Rectangle(0, 0, 20, height));
rectangles.add(new Rectangle(width-20, 0, 20, height));

rectangles.add(new Rectangle(0, height*.33, width, 20));
rectangles.add(new Rectangle(width*0.33, 0, 20, height));

rectangles.add(new Rectangle(0, height*.66, width, 20));
rectangles.add(new Rectangle(width0.66, 0, 20, height));/

rectangles.add(new Rectangle(0, 0, width, 20));
rectangles.add(new Rectangle(0, GroundY, width, 20));
rectangles.add(new Rectangle(0, 0, 20, height));
rectangles.add(new Rectangle(width-20, 0, 20, height));

rectangles.add(new Rectangle(70, 300, 200, 20));
rectangles.add(new Rectangle(70, 260, 130, 20));
rectangles.add(new Rectangle(400, 300, 80, 20));

rectangles.add(new Rectangle(530, 100, 50, 20));
rectangles.add(new Rectangle(510, 120, 50, 20));
rectangles.add(new Rectangle(490, 140, 50, 20));
rectangles.add(new Rectangle(470, 160, 50, 20));
rectangles.add(new Rectangle(450, 180, 50, 20));
}

class Rectangle {
float x;
float y;
float rectWidth;
float rectHeight;

public Rectangle(float x, float y, float rectWidth, float rectHeight) {
this.x = x;
this.y = y;
this.rectWidth = rectWidth;
this.rectHeight = rectHeight;
}
}

void keyPressed() {
if (keyCode == UP) {
upPressed = true;
}
else if (keyCode == DOWN) {
downPressed = true;
}
else if (keyCode == LEFT) {
leftPressed = true;
}
else if (keyCode == RIGHT) {
rightPressed = true;
}
}

void keyReleased() {
if (keyCode == UP) {
upPressed = false;
}
else if (keyCode == DOWN) {
downPressed = false;
}
else if (keyCode == LEFT) {
leftPressed = false;
}
else if (keyCode == RIGHT) {
rightPressed = false;
}
}

any suggestions?

I have a similar question, although its not the same code. I am using Konva to drag and detect collision, that works, but what I want to do is prevent to be able to drag over an item when the collision has been detected, I am not able to.

Sample code:

<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/konva@9.3.15/konva.min.js"></script>
    <meta charset="utf-8" />
    <title>Konva Drag and Drop Collision Detection Demo</title>
    <style>
      body {
        margin: 0;
        padding: 0;
        overflow: hidden;
        background-color: #f0f0f0;
      }
    </style>
  </head>

  <body>
    <div id="container"></div>
    <script>
      var width = window.innerWidth;
      var height = window.innerHeight;

      var stage = new Konva.Stage({
        container: 'container',
        width: width,
        height: height,
      });

      var layer = new Konva.Layer();
      stage.add(layer);

      function createShape() {
        var group = new Konva.Group({
          x: Math.random() * width,
          y: Math.random() * height,
          draggable: true,
        });
        var shape = new Konva.Rect({
          width: 30 + Math.random() * 30,
          height: 30 + Math.random() * 30,
          fill: 'grey',
          rotation: 360 * Math.random(),
          name: 'fillShape',
        });
        group.add(shape);

        var boundingBox = shape.getClientRect({ relativeTo: group });

        var box = new Konva.Rect({
          x: boundingBox.x,
          y: boundingBox.y,
          width: boundingBox.width,
          height: boundingBox.height,
          stroke: 'red',
          strokeWidth: 1,
        });
        group.add(box);
        return group;
      }

      for (var i = 0; i < 10; i++) {
        layer.add(createShape());
      }
      layer.on('dragmove', function (e) {
        var target = e.target;
        var targetRect = e.target.getClientRect();
        layer.children.forEach(function (group) {
          // do not check intersection with itself
          if (group === target) {
            return;
          }
          if (haveIntersection(group.getClientRect(), targetRect)) {
            console.log('tar', target);
            console.log('tarRec', targetRect);
            group.findOne('.fillShape').fill('red');
            target.attrs.x(target._lastPos.x);
          } else {
            group.findOne('.fillShape').fill('grey');
          }
        });
      });

      function haveIntersection(r1, r2) {
        return !(
          r2.x > r1.x + r1.width ||
          r2.x + r2.width < r1.x ||
          r2.y > r1.y + r1.height ||
          r2.y + r2.height < r1.y
        );
      }
    </script>
  </body>
</html>

Nice job on this Kevin, really like the way you demonstrate how these simple interactions are solved with non-obvious solutions. Have you written a similar post use ellipse objects instead of rectangles?