Swept AABB vs Line Segment in 2D

434 views Asked by At

I've been trying to get a swept collision detection up and running now for almost a week and I can't for the life of me get it working. I want to try a moving AABB against a line segment and find out if and when it collides.

Example image

There is a answer on this linked here below: https://gamedev.stackexchange.com/questions/29479/swept-aabb-vs-line-segment-2d/30220#30220?newreg=95e000808fe74acdbd7a6b7d1d4b3a05

I've translated this into Go which will be running the game server and needs this logic:

func SweepRectLine(rectX, rectY, rectW, rectH, rectHSpeed, rectVSpeed, lineX1, lineY1, lineX2, lineY2 float64) (bool, float64) {
    outVel := [2]float64{}
    // hitNormal := [2]float64{}
    lineNX, lineNY := lineX2-lineX1, lineY2-lineY1
    // lineMinX, lineMaxX, lineMinY, lineMaxY := math.Min(lineX1, lineX2), math.Max(lineX1, lineX2), math.Min(lineY1, lineY2), math.Max(lineY1, lineY2)
    var lineMinX, lineMaxX, lineMinY, lineMaxY float64
    if lineNX > 0 {
        lineMinX, lineMaxX = lineX1, lineX2
    } else {
        lineMinX, lineMaxX = lineX2, lineX1
    }
    if lineNY > 0 {
        lineMinY, lineMaxY = lineY1, lineY2
    } else {
        lineMinY, lineMaxY = lineY2, lineY1
    }
    r := (rectW/2)*math.Abs(lineNX) + (rectH/2)*math.Abs(lineNY) //radius to Line
    boxProj := (lineX1-rectX)*lineNX + (lineY1-rectY)*lineNY
    velProj := rectHSpeed*lineNX + rectVSpeed*lineNY

    if velProj < 0 {
        r *= -1
    }

    hitTime := math.Max((boxProj-r)/velProj, 0)
    outTime := math.Min((boxProj+r)/velProj, 1)
    // log.Println("start", hitTime, outTime)

    rectXMax, rectXMin := rectX+rectW/2, rectX-rectW/2
    if rectHSpeed < 0 { // left
        if rectXMax < lineMinX {
            return false, 0
        }
        hitTime = math.Max((lineMaxX-rectXMin)/rectHSpeed, hitTime)
        outTime = math.Min((lineMinX-rectXMax)/rectHSpeed, outTime)
    } else if rectHSpeed > 0 { // right
        if rectXMin > lineMaxX {
            return false, 0
        }
        hitTime = math.Max((lineMinX-rectXMax)/rectHSpeed, hitTime)
        outTime = math.Min((lineMaxX-rectXMin)/rectHSpeed, outTime)
        // log.Println("right", hitTime, outTime)
    } else {
        if lineMinX > rectXMax || lineMaxX < rectXMin {
            return false, 0
        }
    }

    if hitTime > outTime {
        return false, 0
    }

    rectYMax, rectYMin := rectY+rectH/2, rectY-rectH/2
    if rectVSpeed < 0 { // up
        if rectYMax < lineMinY {
            return false, 0
        }
        hitTime = math.Max((lineMaxY-rectYMin)/rectVSpeed, hitTime)
        outTime = math.Min((lineMinY-rectYMax)/rectVSpeed, outTime)
    } else if rectVSpeed > 0 { // down
        if rectYMin > lineMaxY {
            return false, 0
        }
        hitTime = math.Max((lineMinY-rectYMax)/rectVSpeed, hitTime)
        outTime = math.Min((lineMaxY-rectYMin)/rectVSpeed, outTime)
    } else {
        if lineMinY > rectYMax || lineMaxY < rectYMin {
            return false, 0
        }
    }

    if hitTime > outTime {
        return false, 0
    }

    outVel[0] = rectHSpeed * hitTime
    outVel[1] = rectVSpeed * hitTime
    return true, hitTime
}

I also put up a Gist with a executable UI here: https://gist.github.com/KidLinus/3f847c46b0e6dc1addac7252d9cb54ab

I can't for the life of me figure out what all class variables stand for. I think that some of them are native to Unity but that's just a guess. I've come so far as to figure out that the extent is half of the width/height in said direction. What the "line.n" stands for is anyones guess, I assume its the direction and have no idea if it's normalized or not. There are also a couple of dead variables just laying there.

I'd love some help in finishing an implementation in Go for this.

0

There are 0 answers