Search code examples
javacollision-detectionseparating-axis-theorem

Finding the MTV (Minimal Translation Vector) using Separating Axis Theorem


So I've been trying to get collision detection and response working in my game project using the Separating Axis Theorem. I've managed to detect the collision, but for the life of me I cannot manage to figure out how to respond to it. I'm attempting to find the Minimal Translation Vector, but I'm not sure how to make it an actual vector so that I can calculate how to respond to the collision. I've read tutorial after tutorial, and I've looked at many previously asked questions on here, but I can't manage to figure out how to implement it. I'm having a really tough time understanding how to find and use the MTV, so if anybody could really help me understand, and maybe give me some kind of example so I can understand it in implementation rather than just theory, it would be much appreciated, and I apologize for any inconvenience. My code does successfully detect collision, and here it is down below:

//NOTE: This is within the HitPolygon class, so "this" is referencing itself as a polygon
public Projection project(Vector2D axis){

    float min = axis.dot(this.getVertices().get(0));
    float max = min;
    Vector2D vecMax  = new Vector2D(0, 0), vecMin = new Vector2D(0, 0);

    for(int i = 1; i < this.getVertices().size(); i++){
        float p = axis.dot(this.getVertices().get(i));
        if(p < min){
            min = p;
            vecMin = this.getVertices().get(i);
        }
        if(p > max){
            max = p;
            vecMax = this.getVertices().get(i);
        }
    }

    Projection result = new Projection(min, max, vecMin, vecMax, axis);

    return result;

}

public boolean contains(Projection p1, Projection p2){
    return(p1.min >= p2.min && p1.min <= p2.max) || (p1.max >= p2.min && p1.max <= p2.max);
}

public boolean overlap(Projection a, Projection b){
    if(contains(a, b)) return true;
    if(contains(b, a)) return true;

    return false;
}

public boolean collide(HitPolygon b){

    ArrayList<Vector2D> axes1 = this.getAxes(0);
    ArrayList<Vector2D> axes2 = b.getAxes(0);

    for(int i = 0; i < axes1.size(); i++){
        Vector2D axis = axes1.get(i);
        Projection p1 = this.project(axis), p2 = b.project(axis);

        if(!overlap(p1, p2)) return false;
        else{
            float start = p1.min > p2.min ? p1.min : p2.min;
            float end = p1.max < p2.max ? p1.max : p2.max;
            float translation = end - start;
            //translation might be the MTV, more or less, but it's not a vector and I don't know how to turn it into one.
        }
    }

    for(int i = 0; i < axes2.size(); i++){
        Vector2D axis = axes2.get(i);
        Projection p1 = this.project(axis), p2 = b.project(axis);
        if(!overlap(p2, p1)) return false;
        else{

        }
    }
    System.out.println("collide");
    return true;
}

And here's the simple projection class:

class Projection{
    public Vector2D minVec, maxVec;
    public float min, max;
    public Vector2D axis = new Vector2D(0, 0);
    public Projection(float min, float max, Vector2D minVec, Vector2D maxVec, Vector2D axis){
        this.min = min;
        this.max = max;
        this.minVec = minVec;
        this.maxVec = maxVec;
        this.axis = axis;
    }

    public float minDot(){
        return axis.dot(minVec);
    }

    public float maxDot(){
        return axis.dot(maxVec);
    }
}

If any additional code is needed I would be happy to supply it. Forgive my lack of understanding how to find and use the MTV, but any help is truly appreciated. Thank you!


Solution

  • It's very simple actually. You'll need to know the angles of the axes you used and how deep the 2 objects are colliding in each case (the deepness is the length of the area of the projected collision).

    Convert every data to a vector with the following technique:

    var vecX = Math.cos(axisAngle) * deepness;
    var vecY = Math.sin(axisAngle) * deepness;
    

    Then, add together the vectors:

    var sumX = vec1X + vec2X + ...;
    var sumY = vec1Y + vec2Y + ...;
    

    Afted this you get a sum vector, which is the displacement vector you need to use (because of magic)

    enter image description here