Created
May 3, 2019 14:20
-
-
Save Powersaurus/94dbf72d57fdd7501551ff9500948e3b to your computer and use it in GitHub Desktop.
PICO-8 Raycaster Doors demo
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
| 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