Question

i am working since several days on my Collision detection and im getting really code blind at the moment :/. Everything works fine, except the Players Position reset. My Collision Detection Function Returns the intersecting rectangle and 1,2,5 or 6 1=Collision in top-left corner of the Enemy, 2=collision in bottom-left corner of the enemy, 5=collision in bottom-right corner of the enemy and , 6=collision in top-right corner of the enemy. The Information of the player are:

X=Current X Position;
Y=Current Y Position (the higher Y the lower is the Player at the Screen);
X_INIT= the next X Position of the Player;
Y_Init= the next Y Position of the Player;
Player_width= :);
Player_height= :);

The Collision is detected with the NEXT Position of the Player. Now, if the Player is moving from left (or right) he should stop at the enemy. If he is falling from top upon him, the falling should also stop. If he is falling and moving towards him, e.g. jump, he should stop at the left side, or, if he is high enough, he should land on top.

sf::IntRect rect_player(player->initialpos.x, player->initialpos.y, player->size.x, player->size.y);
sf::IntRect rect_tile(pos.x, pos.y, 50, 50);
s_collision_det collision=collide(rect_player,rect_tile);
float x=player->pos.x;
float y=player->pos.y;
float xx=player->initialpos.x;
float yy=player->initialpos.y;
float sx=player->size.x;
float sy=player->size.y;
//float sy=player->size.y;
float stx=collision.colliding_rect.left;
float sty=collision.colliding_rect.top;
float stsx=collision.colliding_rect.width;
//float stsy=collision.colliding_rect.height;
if (collision.collision==1){
cout<<"im in 1++";
    cout << stx<<"##"<< xx+sx;
    if (y>=yy){offset_up(player,rect_tile);}
    //if ((xx+sx>stx)and(sty!=y+sy)){offset_left(player,rect_tile);};

    //if (x+sx>=stx and xx+sx>=stx and y > yy ){offset_down(player,rect_tile);};
}else if (collision.collision==2){
  cout<<"im in 2++";
 if ((x+sx<=stx or xx+sx>stx)and(sty!=y+sy)){offset_left(player,rect_tile);};
    //if ((x>=stx+stsx or xx>stx+stsx) and (sty!=y+sy)){offset_right(player,rect_tile);};
    //if (x+sx>=stx and xx+sx>=stx and y < yy ){offset_up(player,rect_tile);};
    //if (x+sx>=stx and xx+sx>=stx and y > yy ){offset_down(player,rect_tile);};

}else if (collision.collision==5){
cout<<"im in 5++";

if ((x>=stx+stsx or xx>stx+stsx) and (sty!=y+sy)){offset_right(player,rect_tile);};
    //if (x<=stx+stsx and xx<=stx+stsx and y < yy ){offset_up(player,rect_tile);};
    //if (x<=stx+stsx and xx<=stx+stsx and y > yy ){offset_down(player,rect_tile);};
        }else if (collision.collision==6){
    cout<<"im in 6++";
    if ((x>=stx+stsx or xx>stx+stsx) and (sty!=y+sy)){offset_right(player,rect_tile);};
    //if (x<=stx+stsx and xx<=stx+stsx and y < yy ){offset_up(player,rect_tile);};
    //if (x<=stx+stsx and xx<=stx+stsx and y > yy ){offset_down(player,rect_tile);};
}

The outcommented conditions are some i already tried, and which didn´t work. What is the mistake in my thoughts ? :) Thanks for your Help. PS: Excuse some grammar mistakes in my English, it isnt my Mother Tongue. PSS: The offset functions are:

void offset_left(character *player, sf::IntRect tile){
player->initialpos.x=tile.left-player->size.x;


player->x_speed=0;
cout<<"Offset_Left++";
};
void offset_right(character *player, sf::IntRect tile){
player->initialpos.x=tile.left+tile.width;
player->x_speed=0;
cout<<"Offset_Right++";
};
void offset_up(character *player, sf::IntRect tile){
player->initialpos.y=tile.top-player->size.y;
player->collision.bottom=1;
player->y_speed=0;
cout<<"Offset_Up++";
};
void offset_down(character *player, sf::IntRect tile){
player->initialpos.y=tile.top+tile.height;
player->y_speed=0;
cout<<"Offset_Down++";

};
Was it helpful?

Solution

Some preliminary modeling

I assume all objects in your game (including the player's avatar) have a bounding rectangle.

You want not only to detect collision as a boolean result, but also determine which sides are colliding (or else you won't be able to handle the top/side cases).

Collision detection for two rectangles

Assuming a rectangle is defined by its top left and bottom right points as R(x0, y0, x1, y1) and we have two rectangles A and B,

A and B intersect if and only if

A.x0 <= B.x1 and
B.x0 <= A.x1 and
A.y0 <= B.y1 and
B.y0 <= A.y1

Now this is not enough to detect from which side the collision occured. You must take the previous positions into account to handle the collision properly.

For that, you can compute the relative positions of the two rectangles.

In the general case, you can have 3 possible status for each axis (left/middle/right and above/middle/below). Combining these 3 positions yields 9 possibilities (above left, above, above right, left, collision, right, below left, below, below right).

The 4 composite cases (above left, above right, below left and below right) are a pain to handle, so you can simply prioritize one axis (typically the X axis, so that "above" and "below" only occurs when the player's sprite is entirely above the obstacles/enemies/whatever).

In the end, you should devise a function that will return 5 possible values : left, right, above, below and collide.

The pseudo-code for this function is as follows:

position check_pos (rect a, rect b)
  if (a.x1 < b.x0) return left
  if (a.x0 > b.x1) return right
  if (a.y1 > b.y0) return above
  if (a.y0 < b.y1) return below
  return collide

Now to detect a collision, you have to consider the current and next positions of your moving object. Let's say rectangle a is at position a0 with a speed va, to check collision with b we can do :

position check_collision (a0, va, b)
  a1 = a0 + va // apply speed to a
  current = check_pos (a0, b)
  next    = check_pos (a1, b)
  if (next == collide) return current
  return clear

this will return the side from which a will collide with b. It might return "collide" too, but that would mean that you did not handle the previous time step properly (i.e. you allowed two objects to continue moving so that they remain embedded within each other).

Naive implementation

Since some or all of your objects will be moving, it is more convenient to define your rectangles as a reference point and a (width, height) couple.

Struct Point {
    int x;
    int y;
    Point (int x, int y) : x(x), y(y) {}
    Point operator+ (const Point& p) { return Point (x+p.x, y+p.y); }
};

Struct Rectangle {
    enum position { left, right, top, left, collide, clear };
    Point ref;
    Point size;
    Rectangle (Point ref, Point size) : ref(ref), size(size) {}
    Rectangle (int x, int y, int w, int h) : ref(x,y), size(w,h) {}

    position check_pos (const Rectangle& rect)
    {
        if (ref.x+size.x < rect.ref.x            ) return left;
        if (ref.x        > rect.ref.x+rect.size.x) return right;
        if (ref.y+size.y > rect.ref.y            ) return top;
        if (ref.y        < rect.ref.y+rect.size.y) return bottom;
        return collide;
    } 

    position check_collision (const Point velocity, const Rectangle& rect)
    {
        Rectangle a1 = Rectangle(ref+velocity, size);
        position cur  = check_pos (b);
        position next = a1.check_pos (b);
        if (next == collide) return cur;
        return clear;
    } 
};

This is just pseudo-C++ off the top of my head.
Don't expect it to compile out of the box, but hopefully that should get you started.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top