2

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!

Gyo
  • 57
  • 1
  • 5

1 Answers1

6

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

Bálint
  • 4,009
  • 2
  • 16
  • 27
  • This answer almost completely answers my question! The only problem I'm having at this point is that I'm unsure of how to find the deepness. You told me it's the length of area of the projected collision. I'm assuming that's based on the axis (That's probably implied), but I'm not sure how to get the actual length in units, or pixels basically, because the only thing I can seem to get that's similar to deepness is the translation variable, which gets in the thousands. Does this question make sense? Nonetheless, thank you for your answer, it provided me with still some very helpful information. – Gyo Oct 26 '16 at 16:53
  • @Gyo I add an image – Bálint Oct 26 '16 at 17:55
  • The picture definitely helps me understand it conceptually, but I'm still unsure of how to correctly get acquire the deepness. I'm thinking that I need to get the x and y distance between the two points on the axis (Here's and image I made to explain: [image](http://prntscr.com/czbqgf)) And I'm thinking that the x and y distance together would be the mtv for that axis. Is that right? That's the only way I can think of to this to get the deepness in appropriate units. I apologize if it seems like I'm not getting it, I'm trying though. @Bálint – Gyo Oct 26 '16 at 19:32
  • @Gyo You need to get the deepness for **every** axes, create the vector using the angle of those axes, and add them together. If you know the x and y difference between the 2 points, then finding the distance between them can be done using the `deepness = sqrt(x * x + y * y)` formula. Your current proposed method (where you pretty much do the same but only on one axis) wouldn't work. Taking [this scenario](http://prnt.sc/czcagg) for example. If you'd move the blue shape with the `v` vector, then you wouldn't get what you want. – Bálint Oct 26 '16 at 20:11
  • Also, if you know the x and y positions of both points, then you can get the vector using `Vector2D vec = new Vector2D(pos1.x - pos2.x, pos1.y - pos2.y)`. – Bálint Oct 26 '16 at 20:12
  • Oh I see, that makes sense. I have one last question, and I'm sorry if you said this and I missed it, but my question to implement what you're saying is how do I find the x and y coordinates of the "p1" and "p2" in the image I mentioned previously, so that way I can get the deepness with the way you suggested. Or do I not have to get those points and just use the vertices on the polygons themselves. I apologize if that's a silly question, I'm just wanting to understand completely, but I really do appreciate all the help you're providing even though I still haven't completely gotten it. @Bálint – Gyo Oct 26 '16 at 23:47
  • @Gyo I don't know how you can find that, but looking at your code, you still can find out the deepness by getting the length of the overlapping parts of the 2 projections, then you can find out the angle of the axis you're currently projecting to and then calculate the vector with the formula above – Bálint Oct 27 '16 at 05:58
  • Why are you getting the `x` with `sin` and `y` with `cos`? Isn't it normally the other way around? – Nic Dec 12 '17 at 05:38
  • @QPaysTaxes Sorry, that was a mistake. Not sure how I messed that up – Bálint Dec 12 '17 at 07:49