Last active
June 25, 2021 02:54
-
-
Save andymasteroffish/c34218637ad4c8a78e073ab19734c41b to your computer and use it in GitHub Desktop.
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 29 | |
__lua__ | |
--hand cram | |
--by @andy_makes | |
--playable at https://andymakes.itch.io/hand-cram | |
--********************************** | |
--original, 557 char code | |
--********************************** | |
--[[ | |
t=0g=0r=rnd | |
w=128b=abs | |
o=circ::_::if g>0then | |
pal({5,2},1)print("score:"..t,9,9,7) | |
if(btn(5))t,g=0,0 | |
else | |
if(t<1)a={9,9,88,0,40,0,0,0,0,0,40,0,88} | |
i=btn()pal({7,8},1)t+=1 | |
for k=0,200do | |
o(r(w),r(w),5,0)end | |
for k=0,10do | |
s=2^k | |
l=s*2 | |
a[k+3]+=band(i,l)/l-band(i,s)/s | |
end | |
for k=0,1do | |
x=a[3+k*8]y=a[5+k*8]s=a[k+1]o(x,y,s,1+k)o(x,y,s+2,1+k) | |
for c=0,w do | |
srand(c+t)if r(8)<1then | |
p=1+flr(r(2))z=r(w)o(c,z,4,p) | |
if(sqrt((x-c)^2+(y-z)^2)<s)a[k+1]+=b(1+k-p)/5-.01end | |
end | |
if(k==1 and sqrt((x-a[3])^2+(y-a[5])^2)<s+a[1])g=1 | |
if(b(64-x)>66or 66<b(64-y))g=1end | |
end | |
flip()goto _ | |
--]] | |
--********************************** | |
--expanded, commented code | |
--********************************** | |
--this is the exact same code as above, but with whitespace and line breaks added | |
--and a ton of comments to try to explain what it's doing | |
--variables that change and track game-state | |
t=0 --elapsed time | |
g=0 --game-over. if this is anything other than 0, the game has ended | |
--constants | |
w=128 --screen width. this value comes up enough that it saves chars to define it | |
--functions i use enough that it saves characters to to give them single-letter names | |
r=rnd | |
b=abs | |
o=circ | |
--this is the identifer that goto() will use. | |
--this marks the start of the game loop | |
::_:: | |
--game over stuff | |
--g gets set to 1 when the game ends | |
if g>0 then | |
--using pal to change the colors of what has been drawn to "fade" the shapes and highlight the score text | |
pal({5,2},1) | |
--drawing the score. i found out later that i could have saved chars by using "?" instead of print | |
print("score:"..t,9,9,7) | |
--if x is pressed, i reset the game by setting g and time back to 0 | |
--g is an int instead of a bool to save a few characters | |
if(btn(5)) t,g=0,0 | |
--whenever possible, i shoot for single line if statements because they do not require "then" and "end" | |
--if g is 0, we play the game | |
--this is the main game loop | |
else | |
--if t is 0, that means we're on the first frame and i need to "initialize" the game | |
--this array stores all of the game values for the two circles | |
--slots 1 and 2 are used for the sizes (both start at 9) | |
--most of the rest of it is unused because my input loop creates garbage data | |
--but four of the remaining slots represent the x and y of each circle (set to 40 or 88 to give them starting positions on screen) | |
if(t<1) a={9,9,88,0,40,0,0,0,0,0,40,0,88} | |
--btw, i use "<1" instead of "==0" because it saves a char | |
--getting the input as a bitfield | |
--you can see the docs for this here: https://pico-8.fandom.com/wiki/btn | |
--more on this when we get to input | |
i=btn() | |
--setting the palette used during the game | |
--this does not need to happen every frame | |
--but putting it in the above if statement would require making it multi-line, which require "then" and "end" | |
pal({7,8},1) | |
--advance the time | |
--this acts as the score, but is also used to generate the positions of the dots that move across the screen | |
t+=1 | |
--clearing the screen | |
--i did not use cls() because i wanted some visual noise | |
--this loop draws 200 black circles in random positions | |
--this covers most of the screen, but not all of it, creating trails | |
for k=0,200 do | |
o(r(w),r(w),5,0) | |
end | |
--munro hoberman's work introduced me to this technique | |
--he uses is a lot and to great effect in his tweetcarts | |
--exmaple: https://twitter.com/munrohoberman/status/1345134334232113157 | |
--input | |
--this is probably the most confusing part of the code | |
--i highly recomend reading this post by eli piilonen (@2darray) to get a better understanding of how to read the btn bitfield | |
--https://pastebin.com/MVhr16td | |
--this loop runs through 11 of the 13 bits in the input value | |
--the last two don't matter to me so i don't bother | |
--there are other irrelevant values in the middle, but i need to get through them to get the ones the i care about | |
for k=0,10 do | |
s=2^k --getting a power of two to do a binary and comparison to isolate a single bit for each button that pico-8 tracks | |
l=s*2 --getting the value of the next bit | |
a[k+3]+=band(i,l)/l-band(i,s)/s --comparing these values and modifying the number stored in the array | |
--most of the slots in the array are garbage because we don't care about most pairs of inputs | |
--but a few are important like right-arrow minus left-arrow or down-arrow minus up-arrow | |
--the slots we care about wind up representing the x and y positions of the player circles | |
end | |
--this loop checks both player circles | |
--k==0 is the first player circle, k==1 is the second | |
for k=0,1 do | |
--grab the array values we care about for this circle | |
--and store them in names that are shorter and more readable | |
x=a[3+k*8] | |
y=a[5+k*8] | |
s=a[k+1] --size of the circle | |
--draw the circle twice | |
--the second one is two pixels bigger than the actual size | |
--i just thought this looked nice and helped differentiate the player circles from the obstacles | |
--i redefined circ() as o() at the top | |
o(x,y,s,1+k) | |
o(x,y,s+2,1+k) | |
--this loop goes through each pixel on the screen left to right | |
--it will create obstacles and move them across the screen | |
--the seed for each cycle is set by the x position plus the time | |
--this creates the effect of the "random" obstacles moving cleanly across the screen without needing to make objects | |
--shoutout to alex mckendry (fuzzydunlop) on the wonderville discord for introducing me to this | |
for c=0,w do --c is the x position | |
--reseed random using the x position plus the time | |
srand(c+t) | |
--there is a 1/8 chance of having an obstacle in this column | |
--up at the top i redefined rnd() as r() | |
if r(8)<1 then | |
--p represents the color. this can be 1 or 2 | |
p=1+flr(r(2)) | |
--z is the y position ("y" is already used for the player circle position) | |
z=r(w) | |
--draw the circle | |
o(c,z,4,p) | |
--hit detection | |
--getting this to work as single-line if statement was very handy, but it is a dense line! | |
if(sqrt((x-c)^2+(y-z)^2)<s) a[k+1] += b(1+k-p)/5-.01 | |
--the first part is a distance function, getting the distance using the pythagorean theorem | |
-- sqrt((x-c)^2+(y-z)^2) < s | |
--the second part adjusts the circle's size | |
-- i have to use "a[k+1]" instead of "s" because i need to modify the array, not my copy of the value | |
--so i'm adding to a[k+1] but "b(1+k-p)/5-.01" is also confusing | |
--i previously redifed abs as b, so it's taking the absolute value of 1+k (the circle's color) minus p (the obstacle's color) and then dividing it by 5 | |
--so if it matches it will add 0 divided by 5 (0), if it doesn't match it will add 1/5 (0.2) | |
--but then i subtract 0.01 to give a slight reduction in size if the colors match | |
--so what it actually comes out to is adding 0.2-.01 (0.19) if they don't match and subtracting 0.01 if they do | |
end | |
end | |
--this whole loop runs twice, once for each player circle | |
--this means all obstacles are drawn twice, which is unnecessary and a little wasteful, but it saves characters | |
--finally we check if the game has ended | |
--there are two ways this can happen | |
--the first is the two player circles touching | |
--i only check this in the loop for the second circle (k==1) (note: just realized i could have saved a char by using k>0) | |
--i do the same distance check as above using my x,y values for this player circle, and the array locations of those values for the other player circle | |
--if their distance is less than their combined radius, it's game over, menaing i set g to 1 | |
if(k==1 and sqrt((x-a[3])^2+(y-a[5])^2)<s+a[1]) g=1 | |
--the other way you can lose is by going off the screen | |
--i check this by taking the absolute value of the x and y position to the center of the screen | |
if(b(64-x)>66or 66<b(64-y)) g=1 | |
--that is the end of the code that runs for each player circle | |
end | |
--and that's the end of the game loop as well! | |
end | |
--all that's left to do is draw everything to the screen using flip() | |
flip() | |
--and use goto to hop back to ::_:: | |
goto _ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment