Collision detection with walls and objects

I’m making a 2D shooter game where a player navigates through the house (as shown in image).

I want to add collision detection with walls and objects so that the player doesn’t go over it. Any simple ways to accomplish this?

Not really.

You could make an underlying grid (two dimensional array, see tutorials)

Here you place 0 for every cell he can walk and 1 for wall and 2 for health pack and 3 for weapons

So you store the Player Position also in the grid and check if a wall is ahead. Then he cannot go there

1 Like

@Chrisir suggestion is probably the best and simplest solution to implement, the other methods would be traditional edge checking which depending on the shape youre testing can be quite complex, ie elipses or complex convex or concave shapes or alternatively you can do a pixel color check to see if you are standing on a part of the map which differs from the floor, this would require player orientation info though i think so youre not just testing the players center location but all the points for his outline unless you just wanted to use an approximate rectangle and have that be the player. Think hit box in fps games.

However i think you could get away with approximating all collidable objects as rectangles and the player also, then its just a simple rectangle to rectangle collision, however depending on the number of objects in your scene you may need to make use of some optimisations to avoid speed issues.

All these seem so complicated lol. I’m ditching the idea of objects, I just wanna have collision detection with walls. Please help me with code. I’ve never done this, it’s the first time.

Objects in the long run will make things less complex. But i too found objects a bit daunting at the start.

Ill show you both examples, apologies im working on my phone so i am limited in my typing.

Arraylist<sceneObj> objects = new ArrayList<sceneObj>();
Player player;

void setup(){
   player = new Player(random(400),random(400));
   for(int i=0;i<10;i++){
      sceneObj object = new sceneObj(random(400),random(400)); 
objects.add(object);
   }
   size(400,400);
}

void draw(){
   player.draw();
   for(int i=0;i<10;i++){
      sceneObj o = objects.get(i);

      o.draw();

      if(collide()) o.col = color(0);
      else o.col = color(255);
   }

   
}

Class Player{
   float x,y;
   boolean collide;
   color col = color(255);
Player(float x, float y){
   this.x = x;
   this.y = y;
}

void draw(){
   fill(255,0,0);
   rect(x,y,20,40);
}


void update(){

  if(keyPressed&&keyCode==left arrow){
     x-=5;
  }
  if(keyPressed&&keyCode==right arrow){
     x+=5;
  }
};
};

Class sceneObj{
   float x,y;
   boolean collide;
   color col = color(255);
sceneObj (float x, float y){
   this.x = x;
   this.y = y;
}

void draw(){
   fill(col);
   rect(x,y,20,20);
}

void collide(){
   return player.x>x&&player.x<x+20&&player.y>y&&player.y<y+20
}

I think that should work the only thing you need to do is find the keycode for the arrows and sub them into the key part, and add the y key if statements.

I might have a few other errors as this is untested code.

For more advanced collisions you may want to read up

As @Chrisir has commented, there’s no easy way here.

If you have just a few walls, you might get by with AABB collision –

player_x = 0
player_width = 10
wall_x = 45
wall_width = 30

def draw():
    global player_x
    background(200)
    noStroke()
    fill('#00FF00')
    
    if (player_x+player_width > wall_x and 
        player_x < wall_x+wall_width):
        # fill the wall red if there's a collision
        fill('#FF0000')
    
    # draw the wall in green or red
    rect(wall_x, 0, wall_width, height)
    # draw the player in blue
    fill('#0000FF')
    square(player_x, height/2, player_width)
    player_x += 0.5

Of course, this code doesn’t check y-values, but it’s not difficult to modify the if statement to include those.

preview

However, this approach to collision detection can grow unwieldy very quickly when you’re adding multiple walls, picks-up, etc. You can optimize the code – add some classes, functions, split it into modules, and so on – but @Chrisir’s suggestion is more scalable. You can look into tilemaps for more on the topic. I’ll provide a high-level overview of the concept.

You create a tile-set, like this one from Super Mario Bros, adapted by Michael Hadley.

tileset

You use a list to define your level layout. Some tilemap software can prove handy here.

level = [
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 ],
  [  0,   1,   2,   3,   0,   0,   0,   1,   2,   3,   0 ],
  [  0,   5,   6,   7,   0,   0,   0,   5,   6,   7,   0 ],
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 ],
  [  0,   0,   0,  14,  13,  14,   0,   0,   0,   0,   0 ],
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 ],
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 ],
  [  0,   0,  14,  14,  14,  14,  14,   0,   0,   0,  15 ],
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,  15,  15 ],
  [ 35,  36,  37,   0,   0,   0,   0,   0,  15,  15,  15 ],
  [ 39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39 ]
]

You use that list to render your level. Think: a loop statement to render the tiles using the list values. In the result (depicted below), note how the tiles correspond to the numbers in the list (that correspond to the tile labels above).

result

Now, you can program the collision-detection logic by comparing your player position with the level list. Certain tiles – like tile #14 – are solid, so the player cannot advance to that location.

One of the advantages of tilemaps is that you can generate level layouts quickly!

As @paulgoux has pointed out, defining a few classes will help dramatically optimize your development process.

A tilemap is cool but it seems to me that the OP already has an image.

So the String array I was saying is just invisible behind the scenes.

Respect! Impressive!

I re-wrote it, so it’s fully running

There were a few minor typos.

Chrisir



ArrayList<sceneObj> objects = new ArrayList<sceneObj>();
Player player;

void setup() {
  size(400, 400);

  player = new Player(random(400), random(400));
  for (int i=0; i<10; i++) {
    sceneObj object = new sceneObj(random(400), random(400)); 
    objects.add(object);
  }
  background(111);
}

void draw() {
  background(111); 
  player.draw();
  player.update();

  for (int i=0; i<10; i++) {
    sceneObj o = objects.get(i);

    o.draw();

    if (o.collide())
      o.col = color(0);
    else o.col = color(255);
  }
}

// ============================================================

class Player {
  float x, y;
  boolean collide;
  color col = color(255);

  Player(float x, float y) {
    this.x = x;
    this.y = y;
  }

  void draw() {
    fill(255, 0, 0);
    rect(x, y, 20, 40);
  }

  void update() {
    if (keyPressed&&keyCode==LEFT) {
      x-=5;
    }
    if (keyPressed&&keyCode==RIGHT) {
      x+=5;
    }

    if (keyPressed&&keyCode==UP) {
      y-=5;
    }
    if (keyPressed&&keyCode==DOWN) {
      y+=5;
    }
  }
}

// ============================================================

class sceneObj {

  float x, y;
  boolean collide;
  color col = color(255);

  sceneObj (float x, float y) {
    this.x = x;
    this.y = y;
  }

  void draw() {
    fill(col);
    rect(x, y, 20, 20);
  }

  boolean collide() {
    return
      player.x>x&&
      player.x<x+20&&
      player.y>y&&
      player.y<y+20;
  }
}

here is my version

  • you can edit the map under the image (with the mouse) and save and load (s and l)

  • and hide / show grid with q

  • mover player with cursor



// https://discourse.processing.org/t/collision-detection-with-walls-and-objects/25856

Player player;

PImage level0; 

int [][] myMap;
boolean showMap=true; 

// mouse hold to edit grid
boolean hold; 

void setup() {
  size(900, 900); 
  level0 = loadImage("Capture.jpg");

  println(level0.width) ;
  println(level0.height) ;

  // init myMap  
  myMap = new int [level0.width/10+1][level0.height/10];
  for (int x = 0; x < myMap.length; x++) {
    for (int y = 0; y < myMap[0].length; y++) {
      myMap[x][y]=0;
    }
  }
  println(myMap.length);
  println(myMap[0].length);

  player = new Player(300, 300);
} 

void draw() {
  background(0); 

  image(level0, 
    0, 0);

  if (showMap) {
    for (int x = 0; x < myMap.length; x++) {
      for (int y = 0; y < myMap[0].length; y++) {
        fill(255); 
        text( myMap[x][y], x*10, y*10+13);
        if (hold) {
          if (mouseX>= x*10 &&
            mouseX<= (x+1)*10 &&
            mouseY>= (y-1)*10+13 &&
            mouseY<= (y)*10+13) {
            if (keyPressed)
              myMap[x][y]=0;
            else
              myMap[x][y]=1;
          }
        }
      }
    }
  }

  player.draw();
  player.update();

  String help="Help for Game\n\nThe walls are 1, ways are 0  \nl - load \ns - save\nq - show grid on /off\nMouse to set grid to 1 (save when you are ready!)\n         click mouse plus hold key gives 0 \nplay player with cursor keys ";
  fill(255);
  text(help, 
    level0.width+110, 44);
}

//-------------------------------------------------------------------------------

void keyPressed() {
  switch(key) {

  case's':
    //save
    String[] s1 = new String [myMap[0].length];

    int i=0; 
    // empty s1
    for (String dummyString : s1) { 
      s1[i]="";
      i++;
    }

    // make s1 from myMap
    for (int y = 0; y < myMap[0].length; y++) {
      for (int x = 0; x < myMap.length; x++) {
        s1[ y ] +=  myMap[x][y];
      }
    }  
    saveStrings( "level0.txt", s1);
    println("Saved");
    break;

  case 'l':
    //load
    // empty myMap
    for (int x = 0; x < myMap.length; x++) {
      for (int y = 0; y < myMap[0].length; y++) {
        myMap[x][y]=0;
      }
    }

    String[] sLoad = loadStrings( "level0.txt" ); 
    // printArray(sLoad); 
    // fill myMap
    for (int y = 0; y < myMap[0].length; y++) {
      for (int x = 0; x < sLoad[y].length(); x++) {
        myMap[x][y] = int( sLoad[y].charAt(x)+"" );
      }
    }  
    break;

  case 'q':
    //toggle
    showMap=
      ! showMap; 
    break;
  }//switch
  //
}

void mousePressed() {
  hold=true;
}

void mouseReleased() {
  hold=false;
}

// ============================================================

class Player {
  float x, y;
  boolean collide;
  color col = color(255);

  Player(float x, float y) {
    this.x = x;
    this.y = y;
  }

  void draw() {
    fill(255, 0, 0);
    rect(x, y, 20, 40);
  }

  void update() {

    int posInMyMapX=int(x/10); 
    int posInMyMapY=int(y/10);
    fill(255, 0, 0); // RED  
    // myMap[posInMyMapX][posInMyMapY]=9;
    text( myMap[posInMyMapX][posInMyMapY], 
      x*10, y*10+13);

    if (keyPressed&&keyCode==LEFT) {
      posInMyMapX=int((x-5)/10); 
      posInMyMapY=int(y/10);
      if (myMap[posInMyMapX][posInMyMapY] == 0)
        x-=5;
    }
    if (keyPressed&&keyCode==RIGHT) {
      posInMyMapX=int((x+5)/10); 
      posInMyMapY=int(y/10);
      if (myMap[posInMyMapX][posInMyMapY] == 0)
        x+=5;
    }

    if (keyPressed&&keyCode==UP) {
      posInMyMapX=int((x)/10); 
      posInMyMapY=int((y-5)/10);
      if (myMap[posInMyMapX][posInMyMapY] == 0)
        y-=5;
    }
    if (keyPressed&&keyCode==DOWN) {
      posInMyMapX=int((x)/10); 
      posInMyMapY=int((y+5)/10);
      if (myMap[posInMyMapX][posInMyMapY] == 0)
        y+=5;
    }
  }//method
  //
}//class
//


here is the level

111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111
100000000000000000000010000000000000000000000000111
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100111111111111111111110000000000000000000000000011
100111111111111111111110000000000000000000000000011
100111111111111111111110000000000000000000000001111
100111111111111111111110000000000000000000001111011
100111111111111111111110000000000000000000011111111
100111111111111111111110000000000000000000011111111
100000000000000000000000000000000000000000011110011
100000000000000000000000000000000000000000011110011
100000000000000000000000000000000000000000111000011
100111111111111111111110000000000000000000000000001
100111111111111111111110000000000000000000000000001
100111111111111111111110000000000000000000000000001
100111111111111111111110000000000000000000000000001
100111111111111111111110000000000000000000000000001
100111111111111111111110000000000000000000000000001
100000000000000000000010000000000000000000000000001
100000000000000000000010000000000000000000000000001
100000000000000000000010000000000000000000011111001
100000000000000000000010000000000000000000011111001
100000000000000000000010000000000000000000000000001
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
100000000000000000000010000000000000000000000000011
111111111111111111111111111111000000000000000000011
111111111111111111111111111111111111111111111111111

Chrisir