Turn your 2D game into a 3D game!
That's 50% extra D!
100% guaranteed to work, sometimes, if you're lucky.
Simply paste this into the top of your 2D game, and be the envy of all your 2D coding friends!!!1!1
(OK, I'll stop now.)
This works best for top down games like "Pic-oh mummy!" by @Hokutoy, "Pakutto boy" by @Konimiru (both pictured)
Admittedly it's more of a gimmick/experiment - the result isn't very playable due to not being able to see the whole play area.
I mainly just wanted to see if it would work :)
_={ map=map, spr=spr, sspr=sspr, pset=pset, circ=circ, camera=camera, cx=0, cy=0, cyoff=32, ch=32, d=128, ymid=0, s2w=function(x,y) return (x-_.cx)-64,_.ch,128-(y-_.cy) end, proj=function(x,y,z) local scale=_.d/z return x*scale+64,y*scale+_.ymid,scale end } camera=function(x,y) _.cx=x or 0 _.cy=(y or 0)+_.cyoff end camera(0,0) pset=function(x,y,c) local wx,wy,wz=_.s2w(x,y) if(wz<1)return local px,py=_.proj(wx,wy,wz) _.pset(px,py,c) end circ=function(x,y,r,c) local wx,wy,wz=_.s2w(x,y) if(wz<1)return local px,py,scale=_.proj(wx,wy,wz) _.circ(px,py,r*scale,c) end sspr=function(sx,sy,sw,sh,x,y,w,h,fx,fy) w=w or sw h=h or sh local wx,wy,wz=_.s2w(x+w/2,y+h) if(wz<1)return local px,py,scale=_.proj(wx,wy,wz) local pw,ph=w*scale,h*scale -- sub pixel stuff local x0,x1=flr(px-pw/2),flr(px+pw/2) local y0,y1=flr(py-ph),flr(py) _.sspr(sx,sy,w,h,x0,y0,x1-x0,y1-y0,fx,fy) end spr=function(n,x,y,w,h,fx,fy) if(not n or not x or not y)return -- convert to equivalent sspr() call w=(w or 1)*8 h=(h or 1)*8 local sx,sy=flr(n%16)*8,flr(n/16)*8 sspr(sx,sy,w,h,x,y,w,h,fx,fy) end map=function(cx,cy,x,y,w,h,lyr) cx=cx or 0 cy=cy or 0 x=x or 0 y=y or 0 w=w or 128 h=h or 64 -- near/far corners local fx,fy,fz=_.s2w(x,y) local nx,ny,nz=_.s2w(x,y+h*8) -- clip nz=max(nz,0.5) if(fz<=nz)return -- project local npx,npy,nscale=_.proj(nx,ny,nz) local fpx,fpy,fscale=_.proj(fx,fy,fz) -- clamp npy=min(npy,128) -- rasterise local py=flr(npy) while py>=fpy do -- floor plane intercept local g=(py-_.ymid)/_.d local z=_.ch/g -- map coords local mx,my=cx,(fz-z)/8+cy -- project to get left/right local lpx,lpy,lscale=_.proj(nx,ny,z) local rpx,rpy,rscale=_.proj(nx+w*8,ny,z) -- delta x local dx=w/(rpx-lpx) -- sub-pixel correction local l,r=flr(lpx+0.5)+1,flr(rpx+0.5) mx+=(l-lpx)*dx -- render tline(l,py,r,py,mx,my,dx,0,lyr) py-=1 end end |
EDIT 1: adjusted near plane to avoid numeric overflow. Defaults for omitted map() parameters.
EDIT 2: also 3Dise sspr, pset and circ
EDIT 3: sub-pixel logic
EDIT 4: add map() layer parameter. fix gaps between adjacent sprites.
New version!
I've made a new version called Instant 3D plus! :-).
It should behave exactly like the original, but it's cleaned up quite a bit, and you can call the 3D functions directly to fix the bits that it doesn't get right automatically - or place things in exact 3D positions (like up in the air).
More details in the other post.
It's pretty fantastic to see a 2D project in 3D :) Thank you.
...And sometimes it's not so simple haha.
No refunds!
Here's a few more:
"Mazeborn" by @gymcrash
"Lazarus the lizard" by @Truttle1
"Pico sprint" by rylauchelmi
"Escape from squirrel park" by @Nikkei
"Object system demo" by @Saffith
"D-Day" by @projectSAM
I tried it with my project Delve and it turned out really weird
Really cool stuff though, love how simple it is to install.
I also used it on CoinDash and this is amazing!
@Mot Seeing this has 100% made my day. It's the 3D version of D-Day that I always wanted to get round to making. Now I don't have to - thank you! I can't wait to see how the final Hitler boss works in this...
Love it!
Awesome way to add perspective in a game.
I'll try it!
@projectSAM - It's a cool looking game. Reminds me of "Commando" or "Ikari Warriors" on the old 8-bit computers.
If you specify the width in tiles when you call map() it should clip off the right hand side, so you get a proper column instead of seeing the whole tilemap.
Adding/modifying a few camera() commands makes 3D "Pakutto Boy" quite playable.
Hey, this is super cool. I noticed that you're using a table to store the original functions. Could you not save a little table-lookup overhead by using a do end block instead?
do local _print=print function print(s,x,y,c) --fancy 3D version _print(s,x,y,c) end end |
This way there is no extra cost from dereferencing the table. Regardless, I'm gonna give this a shot as a starter point for a project :)
Wow, that's really cool! Thanks for trying it out on that little lizard game I made! :)
I wonder what it would look like if the PICO-8 IDE was made 3D...
I was planning on playing around with this and seeing what can be done with it on a blank project, but it doesn't appear to be working correctly? The perspective is nothing like in the screenshots. All it is doing is panning the camera.
@Lafolie do you have the latest 0.2.0 version?
There were some tline() issues in earlier versions.
I'm using 0.2.0e (although I noticed 0.2.0f has been released)
@Mot yeah I'm on 0d... I thought I updated to e though?! Strange. I'll update and get back to you.
It's a shame that tline doesn't accept the same optional layers attribute map does. Looks rather weird when these lads are flat on the map where there spawn points are defined and also running about.
Not sure what's going on with this one! Funnily enough Sonic does have running animations normally but it's broken here somehow. This is a really old unfinished project though and I don't really remember what most of the drawing code is doing and I don't have the time to dig into it right now.
@oakreef yeah, I noticed the layers issue too with some of my projects. If a layer parameter was added to the tline() command then it could be done properly.
The sonic one looks like it's not clearing the screen before drawing a frame...? Are you doing something clever, like dirty rectangles?
It's been ages since I touched that code and I don't want to dig through it now but IIRC I never bothered clearing the screen because the first thing it draws anyway is pink rectangle across the entire screen, which would wipe out the previous frame anyway. The intent was to eventually draw a proper background with parallax but I never got around to it.
Just tried your code in my Imi's Dungeon game.
It exposes my tile entities xD.
Not bad! Works decently well on my Breakout game with only one addition for camera()
Oh my God bless you, this is probably the best thing that has happened to me this whole year you are amazing
The one game that I have actually completed gave very amusing results.
Something about the game just hated the 3d code
Very usefull I enjoy it for a future game...
P.s. How about ask ZEP to put that code snippet as internal function in next Pico-8 version? as a poke or an I3D()
this snipped will be very used in the future tough! :)
good for my first 3d racer soon! btw.
This makes my cave game so much more immersive!
I had to add sprites over the top of the map tiles that were walls to "lift" them up. I'm using sprite flags to mark them. I'm going to try and bake that into the function, but it would have to be a second drawing pass because a single map call could included both types.
@Mot wow, thanks for trying out your great coffee on mazeborn... It's given me some perspective (!) on how my 3d version is going...
Can you switch this on and off?
I'm making a game that's part overhead map and part side on platform. The map part may benefit from this angle.
@garryg I made an updated version here with the ability to turn it on/off - plus some other cleanups etc.
function _init() sy={} sc={} for i=0,127 do sy[i]=rnd(128)\1 sc[i]=rnd(16)\1 end end function _update() cls() for i=0,127 do pset(i,sy[i],sc[i]) sy[i]=sy[i]+1 if sy[i]==128 then sy[i]=0 end end print("jet flight at night",26,58,1) print("lib by mot",44,66,1) end |
Outstanding work, @Mot. Real gold star achievement.
Just wanted to state it would be useful to have a keystroke such as [TAB] to flip back and forth between using the 3D LIB and normal 2D graphics. This can be disabled as well.
Here's an example.
I used the newer version of the snippet, which is a bit cleaner and has a few more features (like toggling back to 2D).
Basically you have to use a map, as maps get drawn as a flat plane, whereas sprites are still drawn upright.
@Mot Hope one day you plan to do this code snippet for Picotron. I just imagin how my games like Crazy Position / Crazy Rally deluxe looks like in it.
@BGelais that's actually a good idea.
I'd need to learn Picotron a bit better first though.
[Please log in to post a comment]