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)
}
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
}
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
}
}
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
}
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.