You Can't See Through Walls (Cosplore3D Pt:10)

Chig Beef - Feb 12 - - Dev Community

Intro

This is a series following Cosplore3D, a raycaster game to learn 3D graphics. This project is part of 12 Months 12 Projects, a challenge I set myself.

Finding Out If Lines Intersect

All the information online I found about lines intersecting was always slightly confusing, as in, most of it would make sense, and then there will be one line that is just out of left field. And this isn't just in one example, this is pretty much every single one. I don't want to have an equation like this in my code and not even understand it, so I decided I would ask Chat GPT. I forgot how good Chat GPT was at this stuff, but it gave me some nice Python code (and even translated it to Go for me). Let's have a look over the code it gave me (modified to fit my program).

func line_interset(x1, y1, x2, y2, x3, y3, x4, y4 float64) bool {
    // Calculate the slopes
    m1 := (y2 - y1) / (x2 - x1)
    m2 := (y4 - y3) / (x4 - x3)

    // Parallel (or close to apparently?)
    if math.Abs(m1-m2) < 1e-6 {
        return false
    }

    // Int as in intersection, not integer idiot
    var xInt, yInt float64
    if math.IsInf(m1, 0) {
        xInt = x1
        yInt = m2(x1-x3) + y3
    } else if math.IsInf(m2, 0) {
        xInt = x3
        yInt = m1(x3-x1) + y1
    } else {
        xInt = (m1x1 - m2x3 + y3 - y1) / (m1 - m2)
        yInt = m1*(xInt-x1) + y1
    }

    // Massive check
    return math.Min(x1, x2) <= xInt && xInt <= math.Max(x1, x2) &&
        math.Min(y1, y2) <= yInt && yInt <= math.Max(y1, y2) &&
        math.Min(x3, x4) <= xInt && xInt <= math.Max(x3, x4) &&
        math.Min(y3, y4) <= yInt && yInt <= math.Max(y3, y4)
}
Enter fullscreen mode Exit fullscreen mode

Now we just need to check this with each of the 4 lines of a Tile.

func (t *Tile) check_line_in_tile(x1, y1, x2, y2 float64) bool {
    x3, y3 := t.x, t.y
    x4, y4 := t.x, t.y+t.h

    for x := 0; x < 2; x++ {
        for y := 0; y < 2; y++ {
            if line_interset(x1, y1, x2, y2, x3, y3, x4, y4) {
                return true
            }
            x4 += t.w
            y4 -= t.h
        }
        x3 += t.w
        y3 += t.h
        x4, y4 = t.x, t.y+t.h
    }

    return false
}
Enter fullscreen mode Exit fullscreen mode

The way I've done this creates a weird checking order, but the order doesn't really matter, as long as all 4 sides are being checked.

Ending See Through Walls

Now we need to simply call this function whenever we are trying to see an enemy or something similar.

for i := 0; i < len(tiles); i++ {
    if tiles[i].check_line_in_tile(e.x, e.y, c.x, c.y) {
        // Can't see through a wall
        return
    }
}
Enter fullscreen mode Exit fullscreen mode

This works really when things are in a different position across the x coordinate, but poorly in the y coordinate. Turns out Chat GPT gave me some stinky code, I don't know why, but instead I used this wiki article and wrote this code.

func line_interset(x1, y1, x2, y2, x3, y3, x4, y4 float64) bool {
    t := ((x1-x3)*(y3-y4) - (y1-y3)*(x3-x4)) / ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
    u := -((x1-x2)*(y1-y3) - (y1-y2)*(x1-x3)) / ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))

    if 0 <= t && t <= 1 && 0 <= u && u <= 1 {
        return true
    }

    return false
}
Enter fullscreen mode Exit fullscreen mode

And it works perfectly! And I also implemented this logic for seeing items, and shooting.

Next

These levels have been quite annoying to make, so in the next post we should start making an actual level editor. This level editor will allow us to make a 2D layout of a world, and edit the level's sky and floor color.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .