Collision Detection with Squares/Rectangles

edited May 2017 in Questions about Code

Hello, trying to figure out how to do collision detection with squares/Rectangles unsure how to figure out. I have a base idea on how to find the distance, however i'm unsure on what to compare it to.

void collisionDetection(){
  for(int i = Walls.size()-1; i>=0 ; i--){
    Wall current = Walls.get(i);

    for(int b1 = tank1Bullet.size()-1; b1>=0; b1--){
      tank1Bullet currentBullet = tank1Bullet.get(b1);
      //this is problem the problem
      if(dist(currentBullet.x,currentBullet.y,current.X,current.Y)<(current.Width)){
      tank1Bullet.remove(b1);
      //Walls.remove(i);
      }
    }

class Wall{

  float Width;
  float Height;
  float X;
  float Y;
  Wall(float x,float y,float w, float h){
    Width = w;
    Height = h;
    X = x;
    Y = y;

  }
  void display(){
   fill(0);
   rect(X,Y,Width,Height);
  }

}

  class tank1Bullet extends PVector{
    PVector location;
    float rotation, bullet_Speed;

    tank1Bullet(){
        location = new PVector(tank1Location.x, tank1Location.y);
        rotation = tank1Rotate;
        bullet_Speed = 20;
    }
    void update(){

    location.x = location.x  + sin(rotation)*bullet_Speed;
    location.y = location.y  - cos(rotation)*bullet_Speed;

    //condition to removes the bullet from the arrayList if it is off the screen
    if (location.x > 0 && location.x < width+500 && location.y > 0 && location.x < height+500) {

    }
    else {
      tank1Bullet.remove(b1);
    }

  }
    void draw_Bullet(){
      ellipse(location.x,location.y , 10, 10);
    }
  }

Answers

  • can we have the entire code please

  • Here is an in-depth example of Rectangle / Rectangle collision detection.

    This also works with your case, as a square is a type of rectangle.

  • I can see what they're doing not having any luck tho

  • edited May 2017

    this is what i've got and still don't know why it doesnt work.

    void collisionDetection(){
      for(int i = Walls.size()-1; i>=0 ; i--){
        Wall current = Walls.get(i);
    
        for(int b1 = tank1Bullet.size()-1; b1>=0; b1--){
          tank1Bullet currentBullet = tank1Bullet.get(b1);
          boolean hit = circleRect(currentBullet.x,currentBullet.y,currentBullet.radius,current.X,current.Y,current.Width,current.Height);
          if(hit == true){
            tank1Bullet.remove(b1);
          }
        }
        /*for(int b2 = tank2Bullet.size()-1; b2>0; b2--){
    
        }*/
      }
    }
    boolean circleRect(float cx, float cy, float radius, float rx, float ry, float rw, float rh){
    
      float testX = cx;
      float testY = cy;
    
      if(cx < rx) testX = rx;
      else if (cx > rx+rw) testX = rx+rw;
      if(cy < ry) testY = ry;
      else if (cy > ry+rh) testY = ry+rh;
    
      float distX = cx-testX;
      float distY = cy-testY;
      float distance = sqrt((distX*distX)+(distY*distY));
    
      if(distance <= radius){
        return true;
      }
      return false;
    }
    

    used exactly as he had

  • Collision detection between two squares of different size. Code below.

    The idea is this. Imagine you have two squares with the same dimensions. Now bring them side to side. How do you know if there is collision?

    If you keep your left square fixed in space and move the right square toward the left, you get collision as soon as the left corner of the square (either top or bottom... or the whole edge) is between the edges of the other square. For rectangles and squares, you need to check per dimension (along X first, then Y or vice versa).

    However it is not enough to check the left corner/edge of the rectangle. You need to do the same check for the right corner/edge, in case you approach the fixed square from the other side (going left to right).

    As a mention before, you need to do these checks also along the Y dimension. This is the reason I have two conditionals below, and you need to satisfy both conditionals to have a full collision going on.

    Notice this algorithm is only tested for this example. It might not include all the possible scenarios. It also assumes you are working with squares. This is the reason it is better to use a library as it has proper tested code.

    Kf

    PVector p1=new PVector(0, 0);
    PVector pm=new PVector();
    
    int len1=50;
    int lenM=25;
    
    //===========================================================================
    // PROCESSING DEFAULT FUNCTIONS:
    
    void settings() {
      size(400, 600);
    }
    
    void setup() {
    
      textAlign(CENTER, CENTER);
      rectMode(CENTER);
    
      fill(255);
      strokeWeight(2);
    }
    
    
    
    void draw() {
      background(0);
      translate(width/2, height/2);
      pm.set(mouseX, mouseY);
      pm.sub(width/2, height/2);
    
      fill(0, 255, 0);
      rect(p1.x, p1.y, len1, len1);
    
      if (overlap(p1, len1, pm, lenM))
        fill(255, 255, 0);
    
      rect(pm.x, pm.y, lenM, lenM);
    }
    
    void keyReleased() {
    }
    
    void mouseReleased() {
    }
    
    
    
    //===========================================================================
    // OTHER FUNCTIONS:
    
    boolean overlap(PVector p1, int len1, PVector p2, int len2) {
    
      boolean over=false;
    
      //CORNERS FIG #2
      float  cl=p2.x-len2/2;  //left corner
      float  cr=p2.x+len2/2;  //right corner
    
      if (abs(cl-p1.x)<len1/2.0 || abs(cr-p1.x)<len1/2.0) {   //Left or Right corner inside width of p1?
    
        float  ct=p2.y-len2/2;  //Top corner
        float  cb=p2.y+len2/2;  //Bottom corner
    
        if (abs(ct-p1.y)<len1/2.0 || abs(cb-p1.y)<len1/2.0) {
    
          //PREV conditional can be replaced by:  if (abs(p2.y-p1.y)<len1/2.0+len2/2.0) 
    
          return true;
        }
      }
    
      return over;
    }
    
  • edited May 2017

    how do you do collision with a circle and a square then? because atm i'm trying to figure out when the bullet(the ellipse) hits the wall it removes the bullet. Sorry i just realised i worded my question wrong

  • It is a bit more challenging to write the code but not impossible. I ran out of time today. An easy solution is to draw a square but to model your square as a circle of diameter equal to the side length of the square. In other words:

    ellipse(x1,y1,r,r);
    
    rect(x1,y1,len,len);
    

    but when you check for collisions, you assume the square is a circle and you use the collision detection between two circles:

    if(dist(x1y1,x2,y2)<(r+len)) { ... collision detected...}

    although this model is not totally correct, it will give you a very good approximation. You could continue writing you software based on this approach and if you ever decide to change into something more accurate, you can changed the above line for the proper circle-square collision function.

    Kf

  • how do you do collision with a circle and a square then?

    For a working example, choose the type of collision that you want from this list:

    For example, Circle/Rectangle:

  • edited May 2017

    I've decided to scrap that idea I couldn't figure it out. I'll use your approach @kfajer. Im going to change the walls to circles (idea works just as well and is alot easier)

    PVector tank1Location,tank1Acceleration,tank1Speed;
    float tank1Rotate = 0;
    float tank1FireCounter = 0;
    boolean tank1Shoots = true;
    ArrayList<tank1Bullet> tank1Bullet;
    ArrayList<Wall> Walls;
    int b1;
    PImage tank1Image;
    int n = 9;
    
    void setup(){
      size(1000,800);
      tank1Image = loadImage("ship.gif");
      tank1Location = new PVector(200,400);
      tank1Acceleration = new PVector();
      tank1Speed = new PVector();
      tank1Bullet = new ArrayList<tank1Bullet>();
      Walls = new ArrayList<Wall>();
      for(int i = 0; i < n; i++){
        Walls.add(new Wall(255,new PVector(random(width),random(height))));
    
      }
    }
    
    void draw(){
      background(255);
    
        for(b1 = 0; b1<tank1Bullet.size(); b1++){
        tank1Bullet tank1Shot = tank1Bullet.get(b1);
        tank1Shot.update();
        tank1Shot.draw_Bullet();
      }
        tank1Movement();
      tank1KeyMovement();
      tank1Update();
      tank1Display();
    
    
       for(Wall w : Walls){
        w.display();
      }
      //collisionDetection();
    }
    
    void tank1Display(){
      pushMatrix();
      translate(tank1Location.x, tank1Location.y);
      rotate(tank1Rotate);
      image(tank1Image, -24,-40,tank1Image.width, tank1Image.height);
      popMatrix();
    }
    void tank1KeyMovement(){
    
    
      if (keyPressed && key == CODED){
        if (keyCode == LEFT) {
          tank1Rotate-=0.10;
    
        }
        else if (keyCode == RIGHT){
          tank1Rotate+=0.10;
        }
    
    
    }
    }
    void tank1Movement(){
    
        if(keyPressed && key == CODED){
          if(keyCode == UP){
            tank1Acceleration = new PVector (cos(tank1Rotate-PI/2),sin(tank1Rotate-PI/2));
            tank1Speed.add(tank1Acceleration);
            tank1Speed.limit(3);
            tank1Location.add(tank1Speed);
            }else{
                 tank1Acceleration = new PVector(0,0);
          }
        }
    }
    void tank1Update(){
      if((keyPressed && key == CODED)== true){
        if(keyCode == SHIFT){
          if(tank1Shoots == true){
            tank1Bullet.add(new tank1Bullet(10));
            tank1Shoots = false;
            tank1FireCounter = 0;
          }
        }
      }
      if(tank1Shoots == false){
         tank1FireCounter++;
         if(tank1FireCounter ==8){
           tank1Shoots = true;
         }
      }
    }
    
    /* void collisionDetection(){
      for(int i = Walls.size()-1; i>=0 ; i--){
        Wall current = Walls.get(i);
    
        for(int b1 = tank1Bullet.size()-1; b1>=0; b1--){
    
          tank1Bullet currentBullet = tank1Bullet.get(b1);
    
          if(dist(current.loc.x,current.loc.y,currentBullet.location.x,currentBullet.location.y)<=(current.radius));{
            tank1Bullet.remove(b1);
          }
    
          }
        }
    
    } */
    
    class Wall extends PVector{
    
      float radius;
      PVector loc;
      color c;
    
      Wall(color tempC, PVector loc1){
        c = tempC;
        loc = loc1;
        radius = 60;
      }
    
      void display(){
       stroke(0);
       fill(255,0,0);
       ellipse(loc.x,loc.y, radius,radius);
      }
    
    }
    
      class tank1Bullet extends PVector{
        PVector location;
        float rotation, bullet_Speed;
        float radius;
    
        tank1Bullet(float radi){
            location = new PVector(tank1Location.x, tank1Location.y);
            rotation = tank1Rotate;
            bullet_Speed = 20;
            radius = radi;
        }
        void update(){   //movement of the bullet++++++++++++++++++++++++++++++++++++++++++++++++++++
    
        location.x = location.x  + sin(rotation)*bullet_Speed;
        location.y = location.y  - cos(rotation)*bullet_Speed;
    
        //condition to removes the bullet from the arrayList if it is off the screen
        if (location.x > 0 && location.x < width+500 && location.y > 0 && location.x < height+500) {
    
        }
        else {
          tank1Bullet.remove(b1);
        }
    
      }
        void draw_Bullet(){
          ellipse(location.x,location.y , radius, radius);
        }
      }
    

    This is what i have when the collision function is active it just removes all the walls

  • Answer ✓

    Remove tank1KeyMovement() from draw and replace tank1KeyMovement with keyPressed like this:

    void keyPressed() {
    
      if (keyPressed && key == CODED) {
        if (keyCode == LEFT) {
          tank1Rotate-=0.10;
        } else if (keyCode == RIGHT) {
          tank1Rotate+=0.10;
        }
      }
    }
    

    Another change: You classes don't need to extend from PVector so remove any reference to extends PVector.

    Another change: Your class names should start with a capital letter. Example: Wall. However, functions and variable names, including containers, should start with lower case as it is the case for Walls. It should be "walls".

    The next code is partially complete. You need to handle ship-wall interactions similar to what I do btw bullet-wall interactions.

    A final note: Any information related to your ship should be manage by your ship class. For example, you have an acceleration vector in a global scope. This acceleration vector describes your ship motion. It will be naturally to be defined and managed inside your ship class.

    Kf

    PVector tank1Location, tank1Acceleration, tank1Speed;
    float tank1Rotate = 0;
    float tank1FireCounter = 0;
    boolean tank1Shoots = true;
    ArrayList<tank1Bullet> tank1Bullet;
    ArrayList<Wall> Walls;
    int b1;
    PImage tank1Image;
    int n = 9;
    
    void setup() {
      size(1000, 800);
      tank1Image = loadImage("s.gif");
    
      tank1Location = new PVector(200, 400);
      tank1Acceleration = new PVector();
      tank1Speed = new PVector();
      tank1Bullet = new ArrayList<tank1Bullet>();
    
      Walls = new ArrayList<Wall>();
      for (int i = 0; i < n; i++) {
        Wall w=new Wall(255, new PVector(random(width), random(height)));
        Walls.add(w);
      }
    }
    
    void draw() {
      background(255);
    
      for (Wall w : Walls) {
        w.display();
      }
    
      tank1Movement();
      //tank1KeyMovement();
      tank1Update();
      tank1Display();
    
      for (b1 = 0; b1<tank1Bullet.size(); b1++) {
        tank1Bullet tank1Shot = tank1Bullet.get(b1);
        tank1Shot.update();
        tank1Shot.draw_Bullet();
      }
    
      //collision detection wall-bullets
      for (int b1=tank1Bullet.size()-1; b1>=0; b1--) {
        tank1Bullet sh = tank1Bullet.get(b1);
        boolean col=false;
        for (int w1=Walls.size()-1; w1>=0 && col==false; w1--) {
          Wall awall=Walls.get(w1);
          if (sh.collision(awall.loc.x, awall.loc.y, awall.radius)==true) {
            col=true;
            Walls.remove(w1);
          }
        }
    
        //Remove bullet only after you exit looping the wall container
        if (col==true)
          tank1Bullet.remove(b1);
      }
    
    
      //collision detection wall-ship
    }
    
    void keyPressed() {
    
      if (keyPressed && key == CODED) {
        if (keyCode == LEFT) {
          tank1Rotate-=0.10;
        } else if (keyCode == RIGHT) {
          tank1Rotate+=0.10;
        }
      }
    }
    
    
    
    
    void tank1Display() {
      pushMatrix();
      translate(tank1Location.x, tank1Location.y);
      rotate(tank1Rotate);
      image(tank1Image, -24, -40, tank1Image.width, tank1Image.height);
      popMatrix();
    }
    
    void tank1Movement() {
    
      if (keyPressed && key == CODED) {
        if (keyCode == UP) {
          tank1Acceleration = new PVector (cos(tank1Rotate-PI/2), sin(tank1Rotate-PI/2));
          tank1Speed.add(tank1Acceleration);
          tank1Speed.limit(3);
          tank1Location.add(tank1Speed);
        } else {
          tank1Acceleration = new PVector(0, 0);
        }
      }
    }
    
    void tank1Update() {
      if ((keyPressed && key == CODED)== true) {
        if (keyCode == SHIFT) {
          if (tank1Shoots == true) {
            tank1Bullet.add(new tank1Bullet(10));
            tank1Shoots = false;
            tank1FireCounter = 0;
          }
        }
      }
      if (tank1Shoots == false) {
        tank1FireCounter++;
        if (tank1FireCounter ==8) {
          tank1Shoots = true;
        }
      }
    }
    
    /* void collisionDetection(){
     for(int i = Walls.size()-1; i>=0 ; i--){
     Wall current = Walls.get(i);
    
     for(int b1 = tank1Bullet.size()-1; b1>=0; b1--){
    
     tank1Bullet currentBullet = tank1Bullet.get(b1);
    
     if(dist(current.loc.x,current.loc.y,currentBullet.location.x,currentBullet.location.y)<=(current.radius));{
     tank1Bullet.remove(b1);
     }
    
     }
     }
    
     } */
    
    class Wall {
    
      float radius;
      PVector loc;
      color c;
    
      Wall(color tempC, PVector loc1) {
        c = tempC;
        loc = loc1;
        radius = 60;
      }
    
      void display() {
        stroke(0);
        fill(255, 0, 0);
        ellipse(loc.x, loc.y, radius, radius);
      }
    }
    
    class tank1Bullet {
      PVector location;
      float rotation, bullet_Speed;
      float radius;
    
      tank1Bullet(float radi) {
        location = new PVector(tank1Location.x, tank1Location.y);
        rotation = tank1Rotate;
        bullet_Speed = 20;
        radius = radi;
      }
      void update() {   //movement of the bullet++++++++++++++++++++++++++++++++++++++++++++++++++++
    
        location.x = location.x  + sin(rotation)*bullet_Speed;
        location.y = location.y  - cos(rotation)*bullet_Speed;
    
        //condition to removes the bullet from the arrayList if it is off the screen
        if (location.x > 0 && location.x < width+500 && location.y > 0 && location.x < height+500) {
        } else {
          tank1Bullet.remove(b1);
        }
      }
      void draw_Bullet() {
        ellipse(location.x, location.y, radius, radius);
      }
    
      boolean collision(float xx, float yy, float r) {
        float dd=dist(xx, yy, location.x, location.y);
        if (dd<r+radius)
          return true;
    
        return false;
      }
    }
    
  • Thank you very much. Clear concise answers :)

Sign In or Register to comment.