Created
February 22, 2012 00:45
-
-
Save frosty/1880241 to your computer and use it in GitHub Desktop.
Codea raycaster
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
--# 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