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