Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Codea raycaster
--# Main
-- based on untextured raycaster example: http://lodev.org/cgtutor/raycasting.html
MAPWIDTH = 24
MAPHEIGHT = 24
worldMap = {
{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}
}
posX = 22
posY = 12 --x and y start position
dirX = -1
dirY = 0 --initial direction vector
planeX = 0
planeY = 0.66 --the 2d raycaster version of camera plane
SCALEF=2 -- scale factor
-- for performance reasons, I draw at half resolution to an imqge
-- and then scale it up at the end
time = 0 --time of current frame
oldTime = 0 --time of previous frame
w = WIDTH/SCALEF
h = HEIGHT/SCALEF
FPS=0
meshes = {}
function setup()
-- terribly basic control system
iparameter("Forward", 0, 1, 0)
iparameter("Back", 0, 1, 0)
iparameter("Left", 0, 1, 0)
iparameter("Right", 0, 1, 0)
watch("FPS")
buffer = image(w, h)
-- each vertical line gets drawn as a mesh
-- presumably this could also e done as a single mesh with a load of rects
local sq = { vec2(0,1), vec2(SCALEF,1), vec2(SCALEF,0), vec2(0, 0)}
local tsq = triangulate(sq)
for i = 0, w do
meshes[i] = mesh()
meshes[i].vertices = tsq
end
-- alternative controls - tap the edges of the screen
leftRect = TouchRect(0, 0, 157, HEIGHT, color(0,255,255,20))
rightRect = TouchRect(WIDTH-100, 0, 152, HEIGHT, color(0,255,255,20))
topRect = TouchRect(0, HEIGHT-150, WIDTH, 151, color(255,0,255,20))
bottomRect = TouchRect(0, 0, WIDTH, 157, color(255,255,0,20))
end
prevc = color(45, 59, 28, 255)
prevp = {}
prevmt = nil
prevmb = nil
m = mesh()
-- main loop
function draw()
setContext(buffer)
background(40, 40, 40, 255)
fill(72, 53, 37, 255)
rect(0, 0, WIDTH, h/2)
fill(27, 136, 236, 255)
rect(0, h/2, WIDTH, h/2)
for x = 1, w do
--calculate ray position and direction
cameraX = 2 * x / w - 1 --x-coordinate in camera space
rayPosX = posX
rayPosY = posY
rayDirX = dirX + planeX * cameraX
rayDirY = dirY + planeY * cameraX
--which box of the map we're in
mapX = round(rayPosX)
mapY = round(rayPosY)
--length of ray from current position to next x or y-side
sideDistX = nil
sideDistY = nil
--length of ray from one x or y-side to next x or y-side
deltaDistX = math.sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
deltaDistY = math.sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
perpWallDist = nil
--what direction to step in x or y-direction (either +1 or -1)
stepX = nil
stepY = nil
hit = 0 --was there a wall hit?
side = nil --was a NS or a EW wall hit?
--calculate step and initial sideDist
if (rayDirX < 0) then
stepX = -1
sideDistX = (rayPosX - mapX) * deltaDistX
else
stepX = 1
sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX
end
if (rayDirY < 0) then
stepY = -1
sideDistY = (rayPosY - mapY) * deltaDistY
else
stepY = 1
sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY
end
--perform DDA
while (hit == 0) do
--jump to next map square, OR in x-direction, OR in y-direction
if (sideDistX < sideDistY) then
sideDistX = sideDistX + deltaDistX
mapX = mapX + stepX
side = 0
else
sideDistY = sideDistY + deltaDistY
mapY = mapY + stepY
side = 1
end
--Check if ray has hit a wall
if (worldMap[mapX][mapY] > 0) then hit = 1 end
end
--Calculate distance projected on camera direction (oblique distance will give fisheye effect!)
if (side == 0) then
perpWallDist = math.abs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX)
else
perpWallDist = math.abs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY)
end
--Calculate height of line to draw on screen
lineHeight = math.abs(round(h / perpWallDist))
--calculate lowest and highest pixel to fill in current stripe
drawStart = -lineHeight / 2 + h / 2
if(drawStart < 0) then drawStart = 0 end
drawEnd = lineHeight / 2 + h / 2
if(drawEnd >= h) then drawEnd = h - 1 end
--choose wall color
wcolor = nil
if (worldMap[mapX][mapY] == 1) then
wcolor = color(255,0,0,255)
elseif (worldMap[mapX][mapY] == 2) then
wcolor = color(0, 255, 28, 255)
elseif (worldMap[mapX][mapY] == 3) then
wcolor = color(0, 39, 255, 255)
elseif (worldMap[mapX][mapY] == 4) then
wcolor = color(255, 255, 255, 255)
else
wcolor = color(255, 222, 0, 255)
end
--give x and y sides different brightness
if (side == 1) then
wcolor.r = wcolor.r / 2
wcolor.g = wcolor.g / 2
wcolor.b = wcolor.b / 2
end
--draw the pixels of the stripe as a vertical line
pushMatrix()
meshes[x]:setColors(wcolor)
translate(x, drawStart)
scale(1, drawEnd-drawStart)
meshes[x]:draw()
popMatrix()
end
--timing for input and FPS counter
oldTime = time
time = ElapsedTime
frameTime = (time - oldTime) --frameTime is the time this frame has taken, in seconds
FPS=(1.0 / frameTime) --FPS
moveSpeed = DeltaTime * 2
rotSpeed = DeltaTime * 0.5
--move forward if no wall in front of you
if (Forward == 1) then
if(worldMap[round(posX + dirX * moveSpeed)][round(posY)] == 0) then posX = posX + (dirX * moveSpeed) end
if(worldMap[round(posX)][round(posY + dirY * moveSpeed)] == 0) then posY = posY + (dirY * moveSpeed) end
--move backwards if no wall behind you
elseif (Back == 1) then
if(worldMap[round(posX - dirX * moveSpeed)][round(posY)] == 0) then posX = posX - (dirX * moveSpeed) end
if(worldMap[round(posX)][round(posY - dirY * moveSpeed)] == 0) then posY = posY - (dirY * moveSpeed) end
end
--rotate to the right
if (Right == 1) then
--both camera direction and camera plane must be rotated
oldDirX = dirX
dirX = dirX * math.cos(-rotSpeed) - dirY * math.sin(-rotSpeed)
dirY = oldDirX * math.sin(-rotSpeed) + dirY * math.cos(-rotSpeed)
oldPlaneX = planeX
planeX = planeX * math.cos(-rotSpeed) - planeY * math.sin(-rotSpeed)
planeY = oldPlaneX * math.sin(-rotSpeed) + planeY * math.cos(-rotSpeed)
end
--rotate to the left
if (Left == 1) then
--both camera direction and camera plane must be rotated
oldDirX = dirX
dirX = dirX * math.cos(rotSpeed) - dirY * math.sin(rotSpeed)
dirY = oldDirX * math.sin(rotSpeed) + dirY * math.cos(rotSpeed)
oldPlaneX = planeX
planeX = planeX * math.cos(rotSpeed) - planeY * math.sin(rotSpeed)
planeY = oldPlaneX * math.sin(rotSpeed) + planeY * math.cos(rotSpeed)
end
-- finally draw our buffer image to the screen
setContext()
translate(0,0)
scale(SCALEF)
noSmooth()
spriteMode(CORNER)
sprite(buffer)
end
function round(arg)
return math.floor(arg+0.5)
end
function touched(touch)
if (leftRect:touched(touch)) then
Left = 1
elseif (rightRect:touched(touch)) then
Right = 1
else
Left = 0
Right = 0
end
if (topRect:touched(touch)) then
Forward = 1
elseif (bottomRect:touched(touch)) then
Back = 1
else
Forward = 0
Back = 0
end
end
--# TouchRect
TouchRect = class()
function TouchRect:init(x, y, width, height, col)
self.x = x
self.y = y
self.width = width
self.height = height
self.col = col
end
function TouchRect:draw()
fill(self.col)
noStroke()
rect(self.x, self.y, self.width, self.height)
end
function TouchRect:touched(touch)
if (touch.x > self.x and touch.x < (self.x + self.width) and
touch.y > self.y and touch.y < (self.y + self.height)) then
return true
else
return false
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.