Skip to content

Instantly share code, notes, and snippets.

@Powersaurus
Created May 3, 2019 14:20
Show Gist options
  • Save Powersaurus/94dbf72d57fdd7501551ff9500948e3b to your computer and use it in GitHub Desktop.
Save Powersaurus/94dbf72d57fdd7501551ff9500948e3b to your computer and use it in GitHub Desktop.
PICO-8 Raycaster Doors demo
pico-8 cartridge // http://www.pico-8.com
version 16
__lua__
-- by @powersaurus, based on
-- https://lodev.org/cgtutor/raycasting.html
m_width=10
m_height=10
world={
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,1,2,1,0,1,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,0,0,0,0,2,0,0,1},
{1,0,0,0,0,0,1,0,1,1},
{1,0,0,0,0,0,1,0,0,1},
{1,0,1,1,0,0,1,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
}
doors={
{x=4,y=3,open_pcnt=0,open_vel=0},
{x=7,y=5,open_pcnt=0,open_vel=0}
}
player={
pos={x=5,y=6},
dr={x=1,y=0},
plane={x=0,y=0.66},
v=0.7,
vr=0
}
function rotate_player(player,rot)
local old_dr_x=player.dr.x
player.dr.x=player.dr.x*cos(rot)-player.dr.y*sin(-rot) player.dr.y=old_dr_x*sin(-rot)+player.dr.y*cos(-rot)
local old_plane_x=player.plane.x
player.plane.x=old_plane_x*cos(-rot)-player.plane.y*sin(-rot)
player.plane.y=old_plane_x*sin(-rot)+player.plane.y*cos(-rot)
end
function move_in_dir(player,speed)
local move_x=player.pos.x+player.dr.x*speed
local move_y=player.pos.y+player.dr.y*speed
local move_sq=world
[flr(player.pos.y)]
[flr(move_x)]
if move_sq<1 or
(move_sq==2
and door_for(
flr(move_x),
flr(player.pos.y)).open_pcnt>70) then
player.pos.x=move_x
end
move_sq=world
[flr(move_y)]
[flr(player.pos.x)]
if move_sq<1
or (move_sq==2
and door_for(
flr(player.pos.x),
flr(move_y)).open_pcnt>70) then
player.pos.y=move_y
end
end
function _init()
rotate_player(player,329/360)
end
function _update60()
msg=""
local rot=3/360
local speed=0.1
if btn(0) then --rot left
rotate_player(player,-rot)
end
if btn(1) then --rot right
rotate_player(player,rot)
end
if btn(2) then
move_in_dir(player,speed)
end
if btn(3) then
move_in_dir(player,-speed)
end
if btnp(5) then
is_3d= not is_3d
end
update_doors(doors)
end
function update_doors(doors)
for _,d in pairs(doors) do
local dx=player.pos.x-(d.x+0.5)
local dy=player.pos.y-(d.y+0.5)
local dtd=abs(dx*dx+dy+dy)
-- msg=player.pos.x.." "..player.pos.y.." "..d.x.." "..d.y.." "..dtd
if dtd<2 then
-- msg="door "..d.x.." "..d.y
if d.open_pcnt<100 then
d.open_vel=2
end
else
if d.open_pcnt>0 then
d.open_vel=-2
end
end
d.open_pcnt=clamp(d.open_pcnt+d.open_vel,0,100)
end
end
screen_w=128
msg=""
msg2=""
is_3d=true
function _draw()
if is_3d then
draw_3d()
else
draw_2d()
end
end
function draw_2d()
palt(0,false)
palt(11,true)
cls(5)
-- move to top left
camera(8,8)
-- draw world
for y=1,#world do
for x=1,#world[y] do
local sp=0
if (world[y][x]==1) sp=1
if (world[y][x]==2) sp=2
spr(sp,x*8,y*8)
end
end
--[[for _,d in pairs(doors) do
spr(3,d.x*8,d.y*8)
end]]
local py=player.pos.y*8
local px=player.pos.x*8
local pxe=px+player.dr.x*5
local pye=py+player.dr.y*5
for x=1,screen_w do
local pos,ray,
map_x,step_x,
map_y,step_y,
tex,side,rxe2,rye2=true_cast(player,x,screen_w)
if tex==3 then
if side==0 then
map_x+=step_x/2
else
map_y+=step_y/2
end
end
-- screen coords
rxe2*=8
rye2*=8
line(
px,py,rxe2,rye2,9)
--pset(rxe2,rye2,14)
-- print("dx="..ray.y.." dy="..ray.x,2,90,7)
end
line(px,py,pxe,pye,8)
pset(px,py,10)
-- reset camera
camera()
end
test_x=30
function true_cast(player,x,screen_w)
local pos=player.pos
local dr=player.dr
local plane=player.plane
local cam_x=2*x/screen_w-1
local ray={
x=dr.x+plane.x*cam_x,
y=dr.y+plane.y*cam_x
}
local map_x=flr(pos.x)
local map_y=flr(pos.y)
-- ray casting
local side_x=0
local side_y=0
local delta_x=abs(1/ray.x)
local delta_y=abs(1/ray.y)
-- which way are we stepping
-- across the map cells
local step_x=0
local step_y=0
-- stop vars
local hit=0
local side=0
local rxe2=0
local rye2=0
if ray.x<0 then
step_x=-1
side_x=(pos.x-map_x)*delta_x
elseif ray.x>0 then
step_x=1
side_x=(map_x+1-pos.x)*delta_x
else -- hack
side_x=20000
end
if ray.y<0 then
step_y=-1
side_y=(pos.y-map_y)*delta_y
elseif ray.y>0 then
step_y=1
side_y=(map_y+1-pos.y)*delta_y
else -- hack
side_y=20000
end
local tex=0
while hit==0 do
if side_x<side_y then -- travel hor
side_x+=delta_x
map_x+=step_x
side=0 -- ew
else -- travel vert
side_y+=delta_y
map_y+=step_y
side=1 -- ns
end
tex=world[map_y][map_x]
if tex>0 then
if tex==2 then--door texture
-- weird
local map_x2=map_x
local map_y2=map_y
if (pos.x<map_x) map_x2-=1
if (pos.y>map_y) map_y2+=1
local adj=1
local ray_mult=1
if side==1 then
adj=map_y2-pos.y
ray_mult=adj/ray.y
else
adj=(map_x2-pos.x)+1
ray_mult=adj/ray.x
end
rxe2=pos.x+ray.x*ray_mult
rye2=pos.y+ray.y*ray_mult
local true_delta_x=sqrt(1+(ray.y*ray.y)/(ray.x*ray.x))
local true_delta_y=sqrt(1+(ray.x*ray.x)/(ray.y*ray.y))
if abs(ray.x)<0.01 then
true_delta_x=100
end
if abs(ray.y)<0.01 then
true_delta_y=100
end
-- you only need one x/y component
-- depending on whether you hit a
-- horizontal or vertical wall
local d=door_for(map_x,map_y)
if side==0 then -- vertical north-south
local true_y_step=sqrt(true_delta_x*true_delta_x-1)
local half_step_in_y=rye2+(step_y*true_y_step)/2
if flr(half_step_in_y)==map_y
and half_step_in_y-map_y>d.open_pcnt/100
then
hit=1
end
else -- horizontal east-west
local true_x_step=sqrt(true_delta_y*true_delta_y-1)
local half_step_in_x=rxe2+(step_x*true_x_step)/2
if flr(half_step_in_x)==map_x
and half_step_in_x-map_x>d.open_pcnt/100
then
hit=1
end
end
else
hit=1 --hit a wall
end
end
end
return pos,ray,map_x,step_x,map_y,step_y,tex,side,rxe2,rye2
end
function door_for(x,y)
for _,d in pairs(doors) do
if d.x==x and d.y==y then
return d
end
end
return nil
end
function cast_ray(player,x,screen_w)
local pos,ray,map_x,step_x,map_y,step_y,tex,side=true_cast(player,x,screen_w)
local perp_wall_dist=0
if side==0 then
if tex==2 then
map_x+=step_x/2
end
perp_wall_dist=(map_x-pos.x+(1-step_x)/2)/ray.x
else
if tex==2 then
map_y+=step_y/2
end
perp_wall_dist=(map_y-pos.y+(1-step_y)/2)/ray.y
end
return perp_wall_dist,side,tex
end
function draw_3d()
cls()
rectfill(0,64,128,128,5)
msg2=""
for x=0,screen_w do
local perp_wall_dist,side,tex=cast_ray(player,x,screen_w)
local line_height=flr(128/perp_wall_dist)
local draw_start=clamp(-line_height/2+64,0,128)
local draw_end=clamp(line_height/2+64,0,128)
-- pico8 hacks
if draw_start>=draw_end then
draw_start=0
draw_end=128
end
if draw_end==0 then
draw_end=128
end
local col=1
if side==1 then
col=13
end
if tex==2 then
col=9
end
line(x,draw_start,x,draw_end,col)
end
print(stat(1),0,0,7)
end
function clamp(x,min_val,max_val)
return min(max(x,min_val),max_val)
end
__gfx__
100110110111111122222222b28888bb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
100000110000000124949492b288888b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
001100010111551122424242b28bb88b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
011001000555dd5024949492b28bb88b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
011011100ddd66d122424242b28bb88b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000011001d66665024949492b28bb88b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000001055ddd5022424242b288888b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
110110110111111022222222b28888bb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment