Last active
July 25, 2018 01:37
-
-
Save gabrielkw/e4bc2507cad8ce6fb3f85f07a4fdc7c5 to your computer and use it in GitHub Desktop.
SDL2 raycasting in golang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"github.com/veandco/go-sdl2/sdl" | |
"math" | |
"os" | |
) | |
var winTitle string = "Go-SDL2 Render" | |
var winWidth, winHeight int = 640, 480 | |
type Camera struct { | |
X, Y, Angle, Fov float64 | |
} | |
type Level struct { | |
Height, Width, TileWidth, TileHeight int | |
Tiles [][]rune | |
} | |
// method to a level that casts a ray from a position and returns where the ray hits | |
func (targetLevel *Level) CastRay(x, y, Angle float64) (int, int) { | |
for targetLevel.Tiles[int(y)/targetLevel.TileHeight][int(x)/targetLevel.TileWidth] == '0' { | |
x += math.Cos(Angle) | |
y += math.Sin(Angle) | |
} | |
return int(x), int(y) | |
} | |
// method to a level that returns the side (north/south or lest/west) of a tile a point is | |
func (targetLevel *Level) GetTileSide(x, y int) (side string) { | |
dx := float64(targetLevel.TileWidth/2-x%targetLevel.TileWidth) / float64(targetLevel.TileWidth) | |
dy := float64(targetLevel.TileHeight/2-y%targetLevel.TileHeight) / float64(targetLevel.TileHeight) | |
if math.Abs(float64(dx)) >= math.Abs(float64(dy)) { | |
return "LW" | |
} else { | |
return "NS" | |
} | |
} | |
func CreateLevel(tiles [][]rune) (newLevel *Level) { | |
newLevel = new(Level) | |
newLevel.Tiles = tiles | |
newLevel.Height = len(tiles) | |
newLevel.Width = len(tiles[0]) | |
newLevel.TileWidth = winWidth / newLevel.Width | |
newLevel.TileHeight = winHeight / newLevel.Height | |
fmt.Println("Creating level...", "height:", newLevel.Height, "width:", newLevel.Width, "tilewidth:", newLevel.TileWidth, "tileheight:", newLevel.TileHeight) | |
return newLevel | |
} | |
func run() int { | |
var window *sdl.Window | |
var renderer *sdl.Renderer | |
var event sdl.Event | |
var rect sdl.Rect | |
var err error | |
aux := 64.0 | |
level1 := CreateLevel([][]rune{ | |
{'1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'}, | |
{'1', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1'}, | |
{'1', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1'}, | |
{'1', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1'}, | |
{'1', '0', '3', '0', '0', '0', '0', '0', '0', '0', '2', '2', '0', '0', '0', '1'}, | |
{'1', '0', '3', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1'}, | |
{'1', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1'}, | |
{'1', '1', '1', '1', '0', '0', '0', '0', '0', '0', '2', '2', '0', '0', '0', '1'}, | |
{'1', '0', '0', '1', '0', '0', '0', '0', '0', '0', '2', '2', '0', '0', '0', '1'}, | |
{'1', '0', '1', '1', '0', '0', '0', '0', '0', '0', '2', '2', '0', '0', '0', '1'}, | |
{'1', '0', '0', '0', '0', '0', '0', '4', '0', '0', '0', '0', '0', '0', '0', '1'}, | |
{'1', '0', '0', '0', '0', '0', '0', '4', '0', '0', '0', '0', '0', '0', '0', '1'}, | |
{'1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'}, | |
}) | |
ActiveLevel := level1 | |
ActiveCamera := Camera{200, 200, 0, 1.0472} | |
// create a window | |
window, err = sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, winWidth, winHeight, sdl.WINDOW_SHOWN) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err) | |
return 1 | |
} | |
defer window.Destroy() | |
// create a renderer | |
renderer, err = sdl.CreateRenderer(window, 1, sdl.RENDERER_ACCELERATED) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Failed to create renderer: %s\n", err) | |
return 2 | |
} | |
defer renderer.Destroy() | |
// this variable dictates what should be drawn in the screen. 1 draws a top-down map and 2 draws the perspective of the camera | |
renderType := 2 | |
// main loop | |
running := true | |
for running { | |
// get key presses | |
for event = sdl.PollEvent(); event != nil; event = sdl.PollEvent() { | |
switch t := event.(type) { | |
case *sdl.QuitEvent: | |
running = false | |
case *sdl.KeyDownEvent: | |
/* Key presses debug | |
fmt.Printf("[%d ms] Keyboard\ttype:%d\tsym:%c\tmodifiers:%d\tstate:%d\trepeat:%d\n", | |
t.Timestamp, t.Type, t.Keysym.Sym, t.Keysym.Mod, t.State, t.Repeat)/**/ | |
switch t.Keysym.Sym { | |
case 'a': // Turn left | |
ActiveCamera.Angle -= 0.1 | |
if ActiveCamera.Angle < 0 { | |
ActiveCamera.Angle = 2 * math.Pi | |
} | |
case 'd': // Turn right | |
ActiveCamera.Angle += 0.1 | |
if ActiveCamera.Angle > 2*math.Pi { | |
ActiveCamera.Angle = 0 | |
} | |
case 'w': // Move forwards | |
ActiveCamera.X += math.Cos(ActiveCamera.Angle) * 5 | |
ActiveCamera.Y += math.Sin(ActiveCamera.Angle) * 5 | |
case 's': // Move backwards | |
ActiveCamera.X -= math.Cos(ActiveCamera.Angle) * 5 | |
ActiveCamera.Y -= math.Sin(ActiveCamera.Angle) * 5 | |
case 'p': // Increase field of view | |
ActiveCamera.Fov += 0.1 | |
case 'o': // Decrease field of view | |
ActiveCamera.Fov -= 0.1 | |
case 'r': // Toggle rendering type (map/perspective) | |
if renderType == 1 { | |
renderType = 2 | |
} else { | |
renderType = 1 | |
} | |
case 'm': // Increase aux modifier (debug) | |
aux += 1.0 | |
case 'n': // Decrease aux modifier (debug) | |
aux -= 1.0 | |
} | |
} | |
} | |
// render stuff | |
// start by painting the whole screen black | |
renderer.SetDrawColor(0, 0, 0, 255) | |
renderer.Clear() | |
switch renderType { | |
case 1: | |
// render map | |
rect = sdl.Rect{0, 0, int32(winWidth), int32(winHeight)} | |
renderer.SetDrawColor(179, 108, 57, 255) | |
renderer.FillRect(&rect) | |
for i := 0; i < ActiveLevel.Width; i++ { | |
for j := 0; j < ActiveLevel.Height; j++ { | |
if ActiveLevel.Tiles[j][i] != '0' { | |
rect = sdl.Rect{int32(i * ActiveLevel.TileWidth), int32(j * ActiveLevel.TileHeight), int32(ActiveLevel.TileWidth), int32(ActiveLevel.TileHeight)} | |
switch ActiveLevel.Tiles[j][i] { | |
case '1': | |
renderer.SetDrawColor(179, 57, 57, 255) | |
case '2': | |
renderer.SetDrawColor(45, 136, 45, 255) | |
case '3': | |
renderer.SetDrawColor(34, 102, 102, 255) | |
case '4': | |
renderer.SetDrawColor(179, 57, 179, 255) | |
} | |
renderer.FillRect(&rect) | |
} | |
} | |
} | |
// render rays | |
for i := -(ActiveCamera.Fov / 2); i < ActiveCamera.Fov/2; i += 0.01 { | |
targetX, targetY := ActiveLevel.CastRay(ActiveCamera.X, ActiveCamera.Y, ActiveCamera.Angle+i) | |
switch ActiveLevel.Tiles[targetY/ActiveLevel.TileHeight][targetX/ActiveLevel.TileWidth] { | |
case '1': | |
renderer.SetDrawColor(179, 57, 57, 255) | |
case '2': | |
renderer.SetDrawColor(45, 136, 45, 255) | |
case '3': | |
renderer.SetDrawColor(34, 102, 102, 255) | |
case '4': | |
renderer.SetDrawColor(179, 57, 179, 255) | |
} | |
if ActiveLevel.GetTileSide(targetX, targetY) == "NS" { | |
r, g, b, a, err := renderer.GetDrawColor() | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err) | |
return 1 | |
} | |
r -= 20 | |
g -= 20 | |
b -= 20 | |
renderer.SetDrawColor(r, g, b, a) | |
} | |
renderer.DrawLine(int(ActiveCamera.X), int(ActiveCamera.Y), targetX, targetY) | |
} | |
// render camera object on map | |
rect = sdl.Rect{int32(ActiveCamera.X - 10), int32(ActiveCamera.Y - 10), 20, 20} | |
renderer.SetDrawColor(255, 255, 255, 255) | |
renderer.FillRect(&rect) | |
case 2: | |
rect = sdl.Rect{0, int32(winHeight / 2), int32(winWidth), int32(winHeight / 2)} | |
renderer.SetDrawColor(179, 108, 57, 255) | |
renderer.FillRect(&rect) | |
j := 0 | |
for i := -(ActiveCamera.Fov / 2); i < ActiveCamera.Fov/2; i += (ActiveCamera.Fov / float64(winWidth)) * math.Cos(i) { | |
targetX, targetY := ActiveLevel.CastRay(ActiveCamera.X, ActiveCamera.Y, ActiveCamera.Angle+i) | |
switch ActiveLevel.Tiles[targetY/ActiveLevel.TileHeight][targetX/ActiveLevel.TileWidth] { | |
case '1': | |
renderer.SetDrawColor(179, 57, 57, 255) | |
case '2': | |
renderer.SetDrawColor(45, 136, 45, 255) | |
case '3': | |
renderer.SetDrawColor(34, 102, 102, 255) | |
case '4': | |
renderer.SetDrawColor(179, 57, 179, 255) | |
} | |
if ActiveLevel.GetTileSide(targetX, targetY) == "NS" { | |
r, g, b, a, err := renderer.GetDrawColor() | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err) | |
return 1 | |
} | |
r -= 20 | |
g -= 20 | |
b -= 20 | |
renderer.SetDrawColor(r, g, b, a) | |
} | |
distance := math.Sqrt(math.Pow(ActiveCamera.X-float64(targetX), 2) + math.Pow(ActiveCamera.Y-float64(targetY), 2)) | |
z := distance * math.Cos(i) | |
lineheight := float64(winHeight) / z * 64 | |
renderer.DrawLine(j, winHeight/2-int(lineheight), j, winHeight/2+int(lineheight)) | |
j++ | |
} | |
} | |
// draw rendering to the screen | |
renderer.Present() | |
} | |
return 0 | |
} | |
func main() { | |
os.Exit(run()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment