Created
August 27, 2024 07:24
-
-
Save npocmaka/e87d5c90f692369ed15c03a299a3e92d to your computer and use it in GitHub Desktop.
This file contains hidden or 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/hajimehoshi/ebiten/v2" | |
"github.com/hajimehoshi/ebiten/v2/ebitenutil" | |
"image/color" | |
"log" | |
"math" | |
) | |
const ( | |
screenWidth = 640 | |
screenHeight = 480 | |
mapWidth = 24 | |
mapHeight = 24 | |
) | |
var worldMap = [mapWidth][mapHeight]int{ | |
{1, 1, 1, 1, 1, 1, 1, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0, 0, 0, 1}, | |
{1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 1}, | |
{1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 0, 0, 0, 0, 0, 2, 2, 0, 2, 2, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0, 0, 0, 1}, | |
{1, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 4, 0, 0, 0, 0, 5, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 4, 0, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | |
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, | |
} | |
type Game struct { | |
posX, posY float64 | |
dirX, dirY float64 | |
planeX, planeY float64 | |
} | |
func NewGame() *Game { | |
return &Game{ | |
posX: 22, | |
posY: 12, | |
dirX: -1, | |
dirY: 0, | |
planeX: 0, | |
planeY: 0.66, | |
} | |
} | |
func (g *Game) Update() error { | |
frameTime := 1.0 / 60.0 // Assuming 60 FPS | |
moveSpeed := frameTime * 5.0 | |
rotSpeed := frameTime * 3.0 | |
if ebiten.IsKeyPressed(ebiten.KeyW) { | |
if worldMap[int(g.posX+g.dirX*moveSpeed)][int(g.posY)] == 0 { | |
g.posX += g.dirX * moveSpeed | |
} | |
if worldMap[int(g.posX)][int(g.posY+g.dirY*moveSpeed)] == 0 { | |
g.posY += g.dirY * moveSpeed | |
} | |
} | |
if ebiten.IsKeyPressed(ebiten.KeyS) { | |
if worldMap[int(g.posX-g.dirX*moveSpeed)][int(g.posY)] == 0 { | |
g.posX -= g.dirX * moveSpeed | |
} | |
if worldMap[int(g.posX)][int(g.posY-g.dirY*moveSpeed)] == 0 { | |
g.posY -= g.dirY * moveSpeed | |
} | |
} | |
if ebiten.IsKeyPressed(ebiten.KeyD) { | |
oldDirX := g.dirX | |
g.dirX = g.dirX*math.Cos(-rotSpeed) - g.dirY*math.Sin(-rotSpeed) | |
g.dirY = oldDirX*math.Sin(-rotSpeed) + g.dirY*math.Cos(-rotSpeed) | |
oldPlaneX := g.planeX | |
g.planeX = g.planeX*math.Cos(-rotSpeed) - g.planeY*math.Sin(-rotSpeed) | |
g.planeY = oldPlaneX*math.Sin(-rotSpeed) + g.planeY*math.Cos(-rotSpeed) | |
} | |
if ebiten.IsKeyPressed(ebiten.KeyA) { | |
oldDirX := g.dirX | |
g.dirX = g.dirX*math.Cos(rotSpeed) - g.dirY*math.Sin(rotSpeed) | |
g.dirY = oldDirX*math.Sin(rotSpeed) + g.dirY*math.Cos(rotSpeed) | |
oldPlaneX := g.planeX | |
g.planeX = g.planeX*math.Cos(rotSpeed) - g.planeY*math.Sin(rotSpeed) | |
g.planeY = oldPlaneX*math.Sin(rotSpeed) + g.planeY*math.Cos(rotSpeed) | |
} | |
return nil | |
} | |
func (g *Game) Draw(screen *ebiten.Image) { | |
screen.Fill(color.RGBA{0, 0, 0, 255}) | |
for x := 0; x < screenWidth; x++ { | |
cameraX := 2*float64(x)/float64(screenWidth) - 1 | |
rayDirX := g.dirX + g.planeX*cameraX | |
rayDirY := g.dirY + g.planeY*cameraX | |
mapX := int(g.posX) | |
mapY := int(g.posY) | |
var sideDistX, sideDistY float64 | |
deltaDistX := math.Abs(1 / rayDirX) | |
deltaDistY := math.Abs(1 / rayDirY) | |
var perpWallDist float64 | |
var stepX, stepY int | |
hit := 0 | |
var side int | |
if rayDirX < 0 { | |
stepX = -1 | |
sideDistX = (g.posX - float64(mapX)) * deltaDistX | |
} else { | |
stepX = 1 | |
sideDistX = (float64(mapX) + 1.0 - g.posX) * deltaDistX | |
} | |
if rayDirY < 0 { | |
stepY = -1 | |
sideDistY = (g.posY - float64(mapY)) * deltaDistY | |
} else { | |
stepY = 1 | |
sideDistY = (float64(mapY) + 1.0 - g.posY) * deltaDistY | |
} | |
for hit == 0 { | |
if sideDistX < sideDistY { | |
sideDistX += deltaDistX | |
mapX += stepX | |
side = 0 | |
} else { | |
sideDistY += deltaDistY | |
mapY += stepY | |
side = 1 | |
} | |
if worldMap[mapX][mapY] > 0 { | |
hit = 1 | |
} | |
} | |
if side == 0 { | |
perpWallDist = sideDistX - deltaDistX | |
} else { | |
perpWallDist = sideDistY - deltaDistY | |
} | |
lineHeight := int(float64(screenHeight) / perpWallDist) | |
drawStart := -lineHeight/2 + screenHeight/2 | |
if drawStart < 0 { | |
drawStart = 0 | |
} | |
drawEnd := lineHeight/2 + screenHeight/2 | |
if drawEnd >= screenHeight { | |
drawEnd = screenHeight - 1 | |
} | |
col := getColor(worldMap[mapX][mapY], side) | |
ebitenutil.DrawLine(screen, float64(x), float64(drawStart), float64(x), float64(drawEnd), col) | |
} | |
ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %0.2f", ebiten.CurrentFPS())) | |
} | |
func getColor(value int, side int) color.Color { | |
var col color.RGBA | |
switch value { | |
case 1: | |
col = color.RGBA{255, 0, 0, 255} // Red | |
case 2: | |
col = color.RGBA{0, 255, 0, 255} // Green | |
case 3: | |
col = color.RGBA{0, 0, 255, 255} // Blue | |
case 4: | |
col = color.RGBA{255, 255, 255, 255} // White | |
default: | |
col = color.RGBA{255, 255, 0, 255} // Yellow | |
} | |
if side == 1 { | |
col.R /= 2 | |
col.G /= 2 | |
col.B /= 2 | |
} | |
return col | |
} | |
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { | |
return screenWidth, screenHeight | |
} | |
func main() { | |
ebiten.SetWindowSize(screenWidth, screenHeight) | |
ebiten.SetWindowTitle("Raycaster in Ebiten") | |
if err := ebiten.RunGame(NewGame()); err != nil { | |
log.Fatal(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment