No. 1: Circle Sweep
cls() t=0 c=1 while 1 do m=64 x=30*cos(t) y=30*sin(t) z=60*cos(1.5*t) w=60*sin(1.5*t) line(m+x,m+y,m+z,m+w,c) t+=0.00003 c+=0.0003 end
No. 2: Circle Sweep 2
30/06/2016. 129 chars. This is a modified version of my first cart which tweaks the parameters a bit. The big change is obviously the kaleidoscope effect, which is what that poke does. |
poke(24364,7) t=0 while 1 do x=20*cos(t) y=20*sin(t) z=50*cos(0.3*t) w=50*sin(0.3*t) line(30+x,30+y,30+z,30+w,t*2) t+=0.00003 end
No. 3: Curtain
04/07/2016. 140 chars. The version which is all on one line is the true submission, there wasn't room for newline characters! The multi-line version is just to make the code more legible. This was inspired by the vertical raster bars demoscene effect. |
t=73p={t}n=128 function _draw()cls()for i=n,2,-1 do p[i]=p[i-1]x=p[n-i]or 0rect(x,n-i,x+1,n,i)end t+=0.02 p[1]+=sin(t)+sin(t/2)+cos(t/3) end
t=73 p={t} n=128 function _draw() cls() for i=n,2,-1 do p[i]=p[i-1] x=p[n-i] or 0 rect(x,n-i,x+1,n,i) end t+=0.02 p[1]+=sin(t)+sin(t/2)+cos(t/3) end
No. 4: Interdimensional Cliffs
t=73p={t}n=128 function _draw()for i=n,2,-1 do p[i]=p[i-1]x=p[n-i]or 0rect(x,n-i,x+1,n,i)end t+=0.02 p[1]+=sin(t)+sin(t/2)+cos(t/3) end
t=73 p={t} n=128 function _draw() for i=n,2,-1 do p[i]=p[i-1] x=p[n-i] or 0 rect(x,n-i,x+1,n,i) end t+=0.02 p[1]+=sin(t)+sin(t/2)+cos(t/3) end
No. 5: 3D Auto Pong
05/07/2016. 139 chars. At first this was going to be interactive but I couldn't fit player input in anything near 140 characters, so it ended up being automated (there is a bat at the top too, if you look closely). The size and color of the ball are based on the y coordinate to create a 3D effect. The x coordinate follows a sine wave to simulate bouncing off the sides of the screen, while the y coordinate follows an absolute sine wave so that it spends less time at the bottom of the screen and more at the top, which adds to the depth.
|
n=127 t=0::l::cls()t+=0.005 x=64+64*sin(1.6*t)y=n-n*abs(sin(t))line(x-2,0,x+2,0,1)circ(x,y,y/10,8+y/42)rect(x-25,120,x+25,n,12)flip()goto l
n=127 t=0 ::l:: cls() t+=0.005 x=64+64*sin(1.6*t) y=n-n*abs(sin(t)) line(x-2,0,x+2,0,1) circ(x,y,y/10,8+y/42) rect(x-25,120,x+25,n,12) flip() goto l
No. 6: Chars
05/07/2016. 138 chars. One of the many awesome things about the PICO-8 is that it's creator zep hides undocumented bonus features in for the community to find. The kaleidoscope effect from No. 2 is one example, the wide characters here are another. Another trick I learnt from other carts is that in Lua you can assign functions to variables (r=rnd). Because I call the random function rnd() 4 times here (20 chars) I get a saving by assigning a reference to rnd to the variable r and then calling that instead (17 chars).
|
a="\128\132\133\134\135\136\137\138\140\141\143\144\146\147\150\152\153"r=rnd::l::i=r(#a)print(sub(a,i,i),r(122),r(124),r(16))flip()goto l
a="\128\132\133\134\135\136\137\138\140\141\143\144\146\147\150\152\153" r=rnd ::l:: i=r(#a) print(sub(a,i,i),r(122),r(124),r(16)) flip() goto l
No. 7: A Nice Day Out
p=print r=rnd cls(3)rectfill(0,0,127,50,12)p("\143",20,20,10)for i=1,50 do p("\149",r(120),49+r(78),8+r(4))end p("\138",r(119),45+r(80),6)
p=print r=rnd cls(3) rectfill(0,0,127,50,12) p("\143",20,20,10) for i=1,50 do p("\149",r(120),49+r(78),8+r(4)) end p("\138",r(119),45+r(80),6)
No. 8: People
h="\130\134\138\140"b="\137\146"r=rnd::a::for x=0,96,8 do c=1+r(15)i=1+r(4)j=1+r(2) ?sub(h,i,i),x,117,c ?sub(b,j,j),x,121,c end ?"\n" goto a
h="\130\134\138\140" b="\137\146" r=rnd ::a:: for x=0,96,8 do c=1+r(15) i=1+r(4) j=1+r(2) ?sub(h,i,i),x,117,c ?sub(b,j,j),x,121,c end ?"\n" goto a
No. 9: Error
05/07/2016. 140 chars. A fun one inspired of course by the weird thing that old versions of Windows often did when programs crashed. This does actually use mouse controls on PICO-8, which is yet another secret feature found by the community. I had to cheat a little bit by drawing the cursor as a sprite, so this effect isn't pure code. It works simply by not clearing the screen each frame.
|
poke(24365,1)::a::x=stat(32)-25y=stat(33)+2rectfill(x,y,x+50,y+8,12)rect(x,y,x+50,y+8,1)y+=2 print("error",x+2,y,7)spr(1,x+25,y)flip()goto a
poke(24365,1) ::a:: x=stat(32)-25 y=stat(33)+2 rectfill(x,y,x+50,y+8,12) rect(x,y,x+50,y+8,1) y+=2 print("error",x+2,y,7) spr(1,x+25,y) flip() goto a
No. 10: Error Sprite
poke(24365,1) palt(0,f) palt(14,1) ::a:: x=stat(32) y=stat(33) if(stat(34)>0)sspr(8,0,47,17,x-27,y-2) spr(7,x,y) flip() goto a
No. 11: Punishment
25/09/2016. 140 chars. Writing lines on a chalkboard as punishment. Prints one character per frame then moves to the next line once complete. Once it reaches the bottom it just starts writing over the existing lines, meaning it visually does nothing more but without actually exiting the program, because that makes the terminal reappear.
|
s="i must obey the rules"i=1y=4 cls(3)rect(0,0,127,127,4)function _update()m=i%(#s+1)print(sub(s,m,m),m*4,y,7)i+=1 if(m>=#s)y+=6 y=y%120 end
s="i must obey the rules" i=1 y=4 cls(3) rect(0,0,127,127,4) function _update() m=i%(#s+1) print(sub(s,m,m),m*4,y,7) i+=1 if(m>=#s)y+=6 y=y%120 end
No. 12: Zoom Counter
25/09/2016. 139 chars. Counting up from 1 and zooming each number. The way this works is by first printing the number to the screen, then copying that section of graphics memory into the spritebank memory (at sprite 0), then using sspr to print that area of the spritesheet back to the screen while stretching it over time. I had to use a lot of tricks to make it fit!
|
n=1palt(0,z)::a::cls(n-1) ?n,0,0,n memcpy(0,24576,512)t=0::b::cls(n-1)sspr(0,0,8,8,64-t/2,64-t/3,t,t)t+=4 if(t>128)n+=1 goto a flip()goto b
n=1 palt(0,z) ::a:: cls(n-1) ?n,0,0,n memcpy(0,24576,512) t=0 ::b:: cls(n-1) sspr(0,0,8,8,64-t/2,64-t/3,t,t) t+=4 if(t>128)n+=1 goto a flip() goto b
I have two different labels ::a:: and ::b:: which split the program into 3 sections. The first section (before ::a::) is only executed once; the second section (::a:: to ::b::) is executed at the start of each number; the final section (after ::b::) is executed every frame.
cls() clears the screen to the given color (or black/0 if no color is given). The ? syntax is shorthand for the print function and prints the number n to the screen at (0,0) with color n. There are 16 colors in PICO-8 (0-15) but if you pass in other numbers it will automatically wrap to the correct color, so color 16 is color 0, 17 is 1 etc. So n just keeps counting up and we print text of color n onto a screen of color n-1.
Now we copy the portion of the graphics memory representing the first 8 rows of pixels on the screen into the start of the spritebank memory. The addresses are written in decimal rather than standard hexadecimal because it's shorter. Address 0 is the start of the sprite bank and address 24576 (0x6000) is the start of the screen. One byte represents 2 pixels since with 16 colors each pixel only needs 4 bits. At a resolution of 128x128 that means 64 bytes per row and 512 bytes for 8 consecutive rows, which is the height of a sprite (sprites are 8x8).
The printed number is now in sprite 0 so we can finally start rendering that to the screen. The position starts in the middle of the screen (64, 64) and both x and y decrease as the size (i.e. time) increase, which keeps it centred. Only 2 characters will fit in an 8x8 sprite so unfortunately it won't quite work above 99. Since we copy whole rows of pixels, larger numbers will actually be copied into the spritebank, I just had no characters spare to stretch more than a single 8x8 sprite.
The scaling is always even so it stays nice and smooth, and once large enough we reset and move on to the next number. Finally we call flip() to swap the front and back buffers to avoid flickering and to slow the whole thing down. There's a lot going on for just 140 characters!
No. 13: Pyramids
30/09/2016. 132 chars. This one started out as a general scanline triangle rasterizer. While reducing the character count I made the bottom vertices always use the same y coordinate, then tried centring the top vertex. I liked it so I added color, translated to the bottom centre and even found room for button input to control the generation.
|
n=128::s::h,w=rnd(n),rnd(n)flip()cls()for y=0,h do x=w*y/(2*h)line(64-x,n-h+y,64+x,n-h+y,4+11*(y%2))end::t:: if(btn(4))goto s goto t
n=128 ::s:: h,w=rnd(n),rnd(n) flip()cls() for y=0,h do x=w*y/(2*h) line(64-x,n-h+y,64+x,n-h+y,4+11*(y%2)) end ::t:: if(btn(4))goto s goto t