I’m playing around with Pico 8, making little games and experiments. For this cart I’ve made a working SNES Mode 7 style floor. The trick is in the function tline, which draws a line between two points, sampling from the map data along the way. You give it the map position to start from, and a dx and dy value it should increment by after each pixel. The other main thing is setting a depth value per scanline, the closer to the bottom of the screen the nearer to the players view. The rest is just trigonometry, applying angles to vectors using sin and cos.
Click here to try it out.
Here’s the code, which I’ve tried to comment as much as possible for future reference:
function _init()
--horizontal map loop size
poke(0x5f38,8)
--vertical map loop size
poke(0x5f39,8)
--constants
hrzn=63 --y pos ground starts
spd=0.1
trn_spd=0.01
--camera
cam={h=128,x=5,y=5}
update_a(0)
end
function _update()
if (btn(⬆️)) move(spd)
if (btn(⬇️)) move(spd*-1)
if (btn(⬅️)) turn_left()
if (btn(➡️)) turn_right()
if btn(❎) then
cam.h=min(500,cam.h+1)
end
if btn(🅾️) then
cam.h=max(1,cam.h-1)
end
end
function _draw()
cls(12)
for y=hrzn,128 do
--start sampling the map from
--the top, no matter where the
--horizon is. adding 1 to stop div/0
--division by zero on the next
--line
local ln=y-hrzn+1
--since line increases down
--the screen, depth decreases
--higher line = further away
local dpth=cam.h/ln
--map tile coords of the left
--and right side of the screen
local lx=cam.x+dpth*cam.l.x
local ly=cam.y+dpth*cam.l.y
local rx=cam.x+dpth*cam.r.x
local ry=cam.y+dpth*cam.r.y
local ln_wth=rx-lx
local ln_hgt=ry-ly
tline(
--line coords, full scanline
0,y,128,y,
--map start coords
lx,ly,
--width/pixels=h step
ln_wth/128,
--height/pixels=v step
ln_hgt/128)
end
print("h :"..cam.h)
print("a :"..cam.a)
print("x,y:"..cam.x..","..cam.y)
end
function update_a(a)
local c=cos(a)
local s=sin(a)
cam.a=a%1
cam.c=c
cam.s=s
--left and right edges of
--the screen, 90 fov
--
--(x=s,y=-c) == right vector
--(x=0,y=-1) == forwards
--(x=1,y=0) == right
cam.l={x=c-s,y=s+c}
cam.r={x=c+s,y=s-c}
end
function turn_left()
update_a(cam.a-trn_spd)
end
function turn_right()
update_a(cam.a+trn_spd)
end
function move(spd)
--cosine of angle ==
--x offset of unit circle
cam.x+=cam.c*spd
--sine of angle ==
--y offset of unit circle
cam.y+=cam.s*spd
end