Topdown wall collision, not working

Started by
14 comments, last by BurdenJohn 9 years, 10 months ago

THIS CODE ON THIS POST IS NOW OBSOLETE.

Please refer to this post (current method for movement collisions in a topdown environment):

http://www.gamedev.net/topic/657046-topdown-wall-collision-not-working/?view=findpost&p=5156335

Still having trouble, player gets stuck along the bottom of the blocks while moving to the left and pushing up (player stops moving to the left for some reason).

Using SFML simply for drawing, gameplay is implemented using basic logic

I'm currently trying to do smooth top-down collision in which I use intersecting rectangles to detect

if my player is going to be colliding with a rectangle given his velocity in x- and y-directions.

Here's my rectangle struct:

arectangle.h


struct RECTANGLE{
	int left, top, right, bottom;
};

My block class:


#include <SFML/Graphics.hpp>
#pragma once
class Block{
	sf::RenderWindow* window;
public:
	Block(sf::RenderWindow* _wind, int _x, int _y, int _size);
	sf::RectangleShape mask;
	void draw();
	int x, y;
	int size;

};

The main importance in the block class is x, y, and size (32 in my test case).

Here's my block instances, as well as my player which gets a reference to the blocks vector: (main.cpp)


	vector<Block> blocks;
	Block b1(&window, 96, 32, 32);
	Block b2(&window, 32, 160, 32);
	Block b3(&window, 0, 0, 32);
	blocks.push_back(b1);
	blocks.push_back(b2);
	blocks.push_back(b3);

	SFMLPlayer player(&window, &blocks);

The player is situated at position 32, 32 (with size rectangle 32).

And now here's the collision code, in SFMLPlayer.cpp:


  bool SFMLPlayer::IntersectRect(RECTANGLE* r1, RECTANGLE* r2)
  {
      return ! ( r2->left > r1->right
          || r2->right < r1->left
          || r2->top > r1->bottom
          || r2->bottom < r1->top
          );
  }

  //Gets run at 60fps
  void SFMLPlayer::update(){    
    int room_width = 200; //temp
    int room_height = 200; //temp


    int hmove, vmove;
    hmove = (right - left);
    vmove = (down - up);
    int move_speed = 2;

    bool place_meeting = false;

    for(std::vector<Block>::iterator block = blocks->begin(); block != blocks->end(); ++block) {
      RECTANGLE prect;
      prect.left = this->x + hmove;
      prect.top = this->y + vmove;
      prect.right = prect.left + this->size;
      prect.bottom = prect.bottom + this->size;

      RECTANGLE brect;
      brect.left = block->x;
      brect.top = block->y;
      brect.right = block->x + block->size;
      brect.bottom = block->y + block->size;

      if(IntersectRect(&prect, &brect)){
        place_meeting = true;
        break;
      }
    }


    x += ( hmove * move_speed ) * !place_meeting;
    y += ( vmove * move_speed ) * !place_meeting;
    
    //room boundaries check
    if(x + size + hmove > room_width+1){
      x = room_width - size + 2;
    }
    if(x - hmove < 0-1){
      x = -2;
    }
    if(y + size + vmove > room_height+1){
      y = room_height - size + 2;
    }
    if(y - vmove < 0-1){
      y = -2;
    }
    
    mask.setPosition(x, y);
  }

I'm using a boolean: place_meeting() to detect if my (player + velocity) rectangle is going to hit any of the blocks using my InterSectRect(RECT*, RECT*) function.

And if it is, I set place_meeting to true and break out of the loop.

There are a few problems which I will display in an image:

EtcF3XJ.jpg

I can't even get to the left side of the room, I'm blocked out by that block in the bottom left.

Its like its entire left side is expanded to the other parts of the room (as shown in the green marker line in the second picture).

After some more testing I found out that I can't get past the top part of any blocks.

I can get past the bottom parts though.

Here's three more pictures:

WeuTB1F.jpg

Advertisement
With the way you are testing all collisions at once if you are on the bottom of an object you wont be able to move as your function is saying there is a collision you could get round this by seperating the two into x collisions andd y collisions. As to it hitting the imaginary walls have you tried changing

|| r2->top > r1->bottom
|| r2->bottom < r1->top

To
|| r2->top < r1->bottom  // is r2 below r1
|| r2->bottom > r1->top  // is r2 above r1
Sorry if this looks poor im on my phone

Hmm changing my IntersectRect() code to that seems to make my player go through all walls now:


bool SFMLPlayer::IntersectRect(RECTANGLE* r1, RECTANGLE* r2)
{
    return ! ( r2->left > r1->right
        || r2->right < r1->left
        || r2->top < r1->bottom
        || r2->bottom > r1->top
        );
}

EDIT:

All right well I have a better intersection code now, my player finally detects collisions against the walls and doesn't go through them,

however I can't glide across them, my player just gets stuck when I push against the normal of the side of the wall.

Here's my new rectangle intersection code:


bool rectangle_collision(float x_1, float y_1, float width_1, float height_1, float x_2, float y_2, float width_2, float height_2)
{
  return !(x_1 > x_2+width_2 || x_1+width_1 < x_2 || y_1 > y_2+height_2 || y_1+height_1 < y_2);
}

And my for loop using it (with the old intersection function call commented out):


	for(std::vector<Block>::iterator block = blocks->begin(); block != blocks->end(); ++block) {
		RECTANGLE prect;
		prect.left = this->x + hmove;
		prect.top = this->y + vmove;
		prect.right = prect.left + this->size;
		prect.bottom = prect.bottom + this->size;

		RECTANGLE brect;
		brect.left = block->x;
		brect.top = block->y;
		brect.right = block->x + block->size;
		brect.bottom = block->y + block->size;
		/*
		if(IntersectRect(&prect, &brect)){
			place_meeting = true;
			break;
		}
		*/
		if(rectangle_collision(prect.left, prect.top, 32, 32, brect.left, brect.top, 32, 32)){
			place_meeting = true;
			break;
		}
	}

Now by stuck, I mean when I move up to a side of one of the blocks, I can't move anywhere else except the normal (opposite direction to which I'm facing).

Once I move that way, I'm free to move again until I meet a side of the block again.

For example, if my player is moving to the bottom side of a block, and it hits that side, I can't move left or right.

The only direction I can move is down. Once I move down, I'm free to move anywhere I want until I hit another side again.

If I hit the left side of a block (the right side of my player hitting the left side of a block), I can't move up or down,

I can only move left to free myself.

And so on.

Not sure why this is happening, but I'm close.

Just need to be able to glide across the sides.

Search for AABB Collision Detection. The Real Time Collision Detection Book by Christer Ericson goes deep on the subject.

The way i use to move is i check along the x with something like

if(rectangle_collision_x(prect, brect))
{
    place_meeting_x = true;
}
if(rectangle_collision_y(prect, brect))
{
    place_meeting_y = true;
}
 
if(place_meeting_x || place_meeting_y)
{
    break;
}

then have a function to check for x collisions and y collisions such as

bool rectangle_collision_x(const RECTANGLE &r1, const RECTANGLE &r2)
{
    return !((r1.Bottom < r2.Top || r1.Top > r2.Bottom) &&
                  (r1.Left > r2.Right || r1.Right < r2.Left));
}

You will need one for y collisions but that should sort out that you can move left/right if you are hitting the top/bottom of an object.

Hmm it didn't seem to work.

I'm now trying the AABB approach:


	for(std::vector<Block>::iterator block = blocks->begin(); block != blocks->end(); ++block) {
		RECTANGLE prect;
		prect.left = this->x;
		prect.top = this->y;
		prect.right = this->x + this->size;
		prect.bottom = this->y + this->size;

		RECTANGLE brect;
		brect.left = block->x;
		brect.top = block->y;
		brect.right = block->x + block->size;
		brect.bottom = block->y + block->size;

		int deca = 3;
		
		//PLAYER RIGHT TOUCHES BOX LEFT
		//Check if player right side touches box left side
		if( (prect.right + move_speed >= brect.left) && (prect.right + move_speed < brect.left + move_speed*deca) ){
			//Check if we are within the vertical value of the left side
			if ( (prect.top >= brect.top && prect.top <= brect.bottom) || (prect.bottom >= brect.top && prect.bottom <= brect.bottom) ){
				x = brect.left-size-move_speed/(deca-1);
				//continue;
			}
		}

		//PLAYER LEFT TOUCHES BOX RIGHT
		//Check if player left side touches box right side
		if( (prect.left - move_speed <= brect.right) && (prect.left - move_speed > brect.right - move_speed*deca) ){
			//Check if we are within the vertical value of the right side
			if ( (prect.top >= brect.top && prect.top <= brect.bottom) || (prect.bottom >= brect.top && prect.bottom <= brect.bottom) ){
				x = brect.right+move_speed/(deca-1);
				//continue;
			}
		}

		//PLAYER BOTTOM TOUCHES BOX TOP
		//Check if player bottom side touches box top side
		if( (prect.bottom + move_speed >= brect.top) && (prect.bottom + move_speed < brect.top + move_speed*deca) ){
			//Check if we are within the horizontal value of the bottom side
			if( (prect.left >= brect.left && prect.left <= brect.right) || (prect.right >= brect.left && prect.right <= brect.right) ){
				y = brect.top-size-move_speed/(deca-1);
				//continue;
			}
		}

		//PLAYER TOP TOUCHES BOX BOTTOM
		//Check if player top side touches box bottom side
		if( (prect.top - move_speed <= brect.bottom) && (prect.top - move_speed > brect.bottom - move_speed*deca) ){
			//Check if we are within the horizontal value of the bottom side
			if( (prect.left >= brect.left && prect.left <= brect.right) || (prect.right >= brect.left && prect.right <= brect.right) ){
				y = brect.bottom+move_speed/(deca-1);
				//continue;
			}
		}
	}

Unfortunately, this seems to work only for blocks that are alone and not touching another.

If I place a line of blocks, and my player starts gliding along it (say, the top of my player glides along the bottom of the line of blocks),

once my player hits the edge of a block, he stops.

This is taking me way longer than it should, I should all ready be coding other parts of the game, but this is completely blocking me.


bool rectangle_collision_x(const RECTANGLE &r1, const RECTANGLE &r2)
{
return !((r1.bottom < r2.top || r1.top > r2.bottom) &&
(r1.left > r2.right || r1.right < r2.left));
}

bool rectangle_collision_y(const RECTANGLE &r1, const RECTANGLE &r2)
{
return !((r1.right < r2.left || r1.left > r2.right) &&
(r1.top > r2.bottom || r1.bottom < r2.top));
}

bool rectangle_collision_x(const RECTANGLE &r1, const RECTANGLE &r2)
{
return !(r1.bottom > r2.top || r1.top < r2.bottom) ||  // check if r1 is above or below r2
    ((r1.bottom < r2.top || r1.top > r2.bottom) &&      // if it isnt make sure it is to left or right of it
    (r1.left > r2.right || r1.right < r2.left)));
}

bool rectangle_collision_y(const RECTANGLE &r1, const RECTANGLE &r2)
{
return !(r1.left > r2.right || r1.right < r2.left) ||   // check if r1 is to left or right of r2
    ((r1.right > r2.left || r1.left < r2.right) &&      // if not make sure it is above or below
    (r1.top > r2.bottom || r1.bottom < r2.top)));
}

Sorry i am tired and on my phone but checking out

http://www.gamedev.net/page/resources/_/technical/game-programming/collision-detection-r735 it might help

The issue seems to be that whatever code detects a collision is holding you in a collision so that you cannot move, because it isn't properly repositioning the player square at a point where there is no collision occuring.

Hello , for stuck collision have you tried this, havign 2 player positions,one right now and the other one just a moment before it , this way you can have:

if( Player Collides with box){

revert to the position before colision

}

,thats how i have solved it a couple years ago in XNA.

I'm sorry I just couldn't get your rectangle_collision_x/y() functions working.

I'm extremely close now though, but still stuck.

Here's my current collision code:


	for(std::vector<Block>::iterator block = blocks->begin(); block != blocks->end(); ++block) {
		RECTANGLE prect;
		prect.left = this->x;
		prect.top = this->y;
		prect.right = this->x + this->size;
		prect.bottom = this->y + this->size;

		RECTANGLE brect;
		brect.left = block->x;
		brect.top = block->y;
		brect.right = block->x + block->size;
		brect.bottom = block->y + block->size;
		
		//AABB collision approach
		int deca = 3;
		
		//PLAYER RIGHT TOUCHES BOX LEFT
		//Check if player right side touches box left side
		if( (prect.right + move_speed >= brect.left) && (prect.right + move_speed < brect.left + move_speed*deca) ){
			//Check if we are within the vertical value of the left side
			if ( (prect.top >= brect.top && prect.top <= brect.bottom) || (prect.bottom >= brect.top && prect.bottom <= brect.bottom) ){
				x = brect.left-size-move_speed/(deca-1);
				//continue;
			}
		}

		//PLAYER LEFT TOUCHES BOX RIGHT
		//Check if player left side touches box right side
		else if( (prect.left - move_speed <= brect.right) && (prect.left - move_speed > brect.right - move_speed*deca) ){
			//Check if we are within the vertical value of the right side
			if ( (prect.top >= brect.top && prect.top <= brect.bottom) || (prect.bottom >= brect.top && prect.bottom <= brect.bottom) ){
				x = brect.right+move_speed/(deca-1);
				//continue;
			}
		}

		//PLAYER BOTTOM TOUCHES BOX TOP
		//Check if player bottom side touches box top side
		else if( (prect.bottom + move_speed >= brect.top) && (prect.bottom + move_speed < brect.top + move_speed*deca) ){
			//Check if we are within the horizontal value of the bottom side
			if( (prect.left >= brect.left && prect.left <= brect.right) || (prect.right >= brect.left && prect.right <= brect.right) ){
				y = brect.top-size-move_speed/(deca-1);
				//continue;
			}
		}

		//PLAYER TOP TOUCHES BOX BOTTOM
		//Check if player top side touches box bottom side
		else if( (prect.top - move_speed <= brect.bottom) && (prect.top - move_speed > brect.bottom - move_speed*deca) ){
			//Check if we are within the horizontal value of the bottom side
			if( (prect.left >= brect.left && prect.left <= brect.right) || (prect.right >= brect.left && prect.right <= brect.right) ){
				y = brect.bottom+move_speed/(deca-1);
				//continue;
			}
		}
		
	}

So what's happening now is, the sides are perfect! I can glide across the sides very easily!

Now let's say the top of my player (prect.top) is touching the bottom of the wall of blocks.

I can glide to the right, though when it hits edges, it jitters a bit, but it still glides, but the jitterness still needs to be fixed.

However if I glide to the left and I hit edges, the player stops.

The same as when the bottom of my player (prect.bottom) is touching the top of the wall of blocks (brect.top),

gliding to the right works, but jittery (not smooth) when it hits edges, gliding to the left, when you hit an edge, player stops.

My top and bottom block collision checks are under the following comments:
//PLAYER BOTTOM TOUCHES BOX TOP

and

//PLAYER TOP TOUCHES BOX BOTTOM

EDIT:
I just noticed that since the player is stopping in the x-coordinate, the two statements that are causing it are the ones under these comments:

//PLAYER RIGHT TOUCHES BOX LEFT

//PLAYER LEFT TOUCHES BOX RIGHT

I'm extremely close to finally getting it perfect, just need to fix:
- Jitterness when gliding to the right against bottom/top of blocks, only when the player hits the edges of a block

- The player stopping when gliding to the left against bottom/top of blocks, only when the player hits the edges of a block

Does anyone know, possibly through the two specified if statements under those two comments I mentioned, what could be causing it and how to fix it?

I've tried a large combination of things.

One thing I did to fix my problem from before was, I change all of the if's to else-ifs.

This topic is closed to new replies.

Advertisement