Question

I've been searching around for the past few days, and researching about Vectors, but still can't quite get my head around the math..

I have two AABB's. On collision I want my method to return a Vector that I can then add to the position Vector to bring my object back into bounds.

Here's my current code:

(The position Vector is the center of the AABB)

public Vector2f collide(Sprite other) {
    if(!collideBoolean(other)) return null; //no collision

    float xAxis = Math.abs(this.position.x - other.getX()); //distance between centers on x axis
    float yAxis = Math.abs(this.position.y - other.getY()); //distance between centers on y axis

    //these combined values show the minimum distance apart the objects need to be to not collide
    int cw = this.getHalfWidth() + other.getHalfWidth(); //combined width
    int ch = this.getHalfHeight() + other.getHalfHeight(); //combined height

    float ox = Math.abs(xAxis - cw); //overlap on x
    float oy = Math.abs(yAxis - ch); //overlap on y

    //direction
    Vector2f dir = new Vector2f(this.position); 
    dir.sub(other.getPosition()); //subtract other.position from this.position
    dir.normalise();

    return new Vector2f(dir.x * ox, dir.y * oy);
}

(self explanatory but here is also the code for collideBoolean(Sprite other) )

public boolean collideBoolean(Sprite other) {
    //code using halfwidths and centers
    if(Math.abs(this.position.x - other.getX()) > (this.getHalfWidth() + other.getHalfWidth())) return false;
    if(Math.abs(this.position.y - other.getY()) > (this.getHalfHeight() + other.getHalfHeight())) return false;

    return true;
}

My current code more or less works.. But the (this) object that is colliding against 'other' gets pushed out AND sides towards the closest corner of 'other'.

I think I'm really close. Surely it'll be something blindingly obvious to a different set of eyes, but I can't quite work it out. Any help would be greatly appreciated!

Thanks,

EDIT:

Adding this into the end of the collide(Sprite other) method works, except isn't as tidy as I'd like. Also when moving into the 'other' body along only one axis it works fine, but if you push into the body at an angle you get inside the shape and it throws you out randomly.

This is probably just because I'm moving too many pixels per step, and I should sweep test my collisions though

(this code looks at the projection vector to see which component is bigger, then 0's out the biggest component. This means that I'm only projecting out of the shape along the shortest path)

    ....
    //direction
    ....

    Vector2f projection = new Vector2f(dir.x * (ox+1), dir.y * (oy+1));
    if(Math.abs(projection.x) > Math.abs(projection.y)) projection.x = 0;
    else if(Math.abs(projection.y) > Math.abs(projection.x)) projection.y = 0;

    return projection;
}

EDIT TWO

With the implementation of Ishtar's answer things were looking good. But I've found if I'm colliding a small object with a wide object, it accurately fixes the collision near the centers, but as you get out near the corners you sink into the shape.

Like this:

         _
 _______l_l_________
|                   |
|______OK___________|


 _--________________
| --                |
|_____SINKS IN______|

EDIT THREE

Current collision code:

public class Collision {

/** fix collision based on mass */
public static void collide(Sprite s1, Sprite s2) {
    float xAxis = Math.abs(s1.getX() - s2.getX()); //distance between centers
    float yAxis = Math.abs(s1.getY() - s2.getY()); //distance between centers

    int cw = s1.getHalfWidth() + s2.getHalfWidth(); //combined width
    int ch = s1.getHalfHeight() + s2.getHalfHeight(); //combined height

    //early exit
    if(xAxis > cw) return;
    if(yAxis > ch) return;

    float ox = Math.abs(xAxis - cw); //overlap on x
    float oy = Math.abs(yAxis - ch); //overlap on y

    if(s1.getMass() <= s2.getMass())
        fixCollision(s1, s2, ox+1, oy+1);    //the +1's make you get out of the shape instead of 
    else //if(s1.getMass() > s2.getMass())    //correcting you onto the edge where you'll be in constant collision
        fixCollision(s2, s1, ox+1, oy+1);
}

/**
 * Fixes the collision
 * @param s1 : this gets pushed out (should be lower mass)
 * @param s2 : this stays where it is
 * @param ox : the overlap along the x axis
 * @param oy : the overlap along the y axis
 */
private static void fixCollision(Sprite s1, Sprite s2, float ox, float oy) {
    //direction
    Vector2f dir = new Vector2f(s1.getPosition()); 
    dir.sub(s2.getPosition());
    dir.normalise();

    Vector2f projection = new Vector2f(dir.x * (ox), dir.y * (oy));
    if(Math.abs(projection.x) > Math.abs(projection.y)) projection.x = 0;
    else if(Math.abs(projection.y) > Math.abs(projection.x)) projection.y = 0;

    if(ox > oy) s1.getPosition().add( new Vector2f(0, dir.y * oy) ); //overlap is bigger on x so project on y
    else if(ox < oy) s1.getPosition().add( new Vector2f(dir.x * ox, 0)); //overlap is bigger on x so project on x
    else s1.getPosition().add( new Vector2f(dir.x * ox, dir.y * oy)); //corner to corner
}
Was it helpful?

Solution

float ox = Math.abs(xAxis - cw); //overlap on x
float oy = Math.abs(yAxis - ch); //overlap on y

//direction
Vector2f dir = new Vector2f(this.position); 
dir.sub(other.getPosition()); //subtract other.position from this.position
dir.normalise();

return new Vector2f(dir.x * ox, dir.y * oy);

The returned translation vector will make both ox (overlap in x) and oy (overlap in y) zero. But that's not what you need. If ox is 0, there is no overlap in the x direction, thus no overlap at all. If oy is 0, idem. So, you need to find the vector that will make only ox or oy zero.

if (ox > oy )
  return new Vector3f(0,dir.y*oy);//top-bottom collision
else if (ox < oy )
  return new Vector3f(dir.x*ox,0);//left-right collision
else //if(ox == oy)
  return new Vector3f(dir.x*ox,dir.y*oy); //corner-corner collision, 
                                          //unlikely with float's.

OTHER TIPS

To solve the "sliding problem" do this: (C# XNA code however)

Vector2 direction = (this.Center - other.Center);
direction.Normalize();

direction.X = (int)Math.Round(direction.X);
direction.Y = (int)Math.Round(direction.Y);

First you get a unit vector with the direction you should exit from. But using that as it is, will be unreliable when you're near the corners. the rounding makes the strongest value to be 1, and the other 0.

Explanation why the rounding is necessary:

For example say the two objects centers on the Y axis are equal and we are exiting left. The direction unit vector is (-1, 0) giving us a reliable number to multiply the overlap with. but as you get closer and closer to the corner of the shape the number turns more into (-0.88, *) that non-whole number, when multiplied leads to losing pixels and you sink into the shape.

I hope the explanation made since, not too great at these things.

By the way, I am the OP ^_^ ..thanks for the help everyone

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