JadeLombax [Lexaloffle Blog Feed]https://www.lexaloffle.com/bbs/?uid=42638 LCD Clock <p> <table><tr><td> <a href="/bbs/?pid=113342#p"> <img src="/bbs/thumbs/pico8_lcd_clock-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=113342#p"> lcd_clock</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=113342#p"> [Click to Play]</a> </td></tr></table> </p> <p>A segmented LCD-style clock. It's pretty simple, but figuring out an efficient way of storing the graphics and not using any spritesheet space was interesting. Entry for the <a href="https://itch.io/jam/pico-8-512-char-jam">Pico-8 512-Char Jam</a>.</p> https://www.lexaloffle.com/bbs/?tid=48215 https://www.lexaloffle.com/bbs/?tid=48215 Sun, 19 Jun 2022 05:26:47 UTC Tetris 1K (Pico1k Jam) <p> <table><tr><td> <a href="/bbs/?pid=97486#p"> <img src="/bbs/thumbs/pico8_tetris_1k-6.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=97486#p"> tetris_1k</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=97486#p"> [Click to Play]</a> </td></tr></table> </p> <p><strong>Version 1.01: Fixed glitch, improved game over screen, corrected next piece preview orientation</strong></p> <p>This is basically a full game of Tetris in 1024 characters, featuring:</p> <ul> <li> <p>Sprite Graphics</p> </li> <li> <p>Next Piece Preview</p> </li> <li> <p>Score System</p> </li> <li> <p>Left/Right Piece Rotation</p> </li> <li> <p>Pieces can be slotted in under ledges</p> </li> <li> <p>Title Screen</p> </li> <li>Game Over State/Restart</li> </ul> <p>Controls:</p> <p>L/R-------Move Piece</p> <p>O---------Rotate Counterclockwise</p> <p>X---------Rotate Clockwise</p> <p>Down---Soft Drop</p> <p>This is an upgrade of my previous Tweettweetjam version of Tetris, and I was able to add a lot of things due to the extra space. A few things that I wanted to include wouldn't fit, like sound effects, level selection, and a fancier title screen, but this has graphics, solid mechanics, and a full game loop. The scoring system is based on the one in the Game Boy version, but with points only given when lines are cleared, and values divided by 10 to fit in Pico-8's 32,767 maximum. Hope you enjoy playing it!</p> https://www.lexaloffle.com/bbs/?tid=44668 https://www.lexaloffle.com/bbs/?tid=44668 Sat, 18 Sep 2021 11:14:51 UTC BBS won't let me upload text for tutorial <p>I'm currently working on a tutorial for fitting sprite graphics into tweetcarts (<a href="https://www.lexaloffle.com/bbs/?tid=44375">https://www.lexaloffle.com/bbs/?tid=44375</a>), but I've run into a problem. I have a lot of text to upload, but the BBS seems to be putting arbitrary limits on how much I can fit into a post, and it's very frustrating.</p> <p>I'm already stretching the tutorial into multiple posts because for some reason it won't let me add any more text to the intial post, but now it won't even let me upload a reasonably-sized bit of code into a post by itself. I put a similarly-sized chunk of code into the previous post, but in the next one I can only post about 60% of that amount. Any more and the little loading animation just endlessly loops and nothing gets uploaded. If anyone can help me out with this, I'd really appreciate it.</p> https://www.lexaloffle.com/bbs/?tid=44390 https://www.lexaloffle.com/bbs/?tid=44390 Fri, 27 Aug 2021 07:15:10 UTC Tweetcart Sprite Graphics Tutorial <p>Since getting into Pico-8 last year, I've enjoyed finding ways to optimize my code and fit a lot in a small space, and this has included making Tweetcarts. Recently, I've found ways to cram detailed sprite graphics into a tweet and have made various animated demos like these:</p> <img style="" border=0 src="/media/42638/jelpi_tweet_0.gif" alt="" /> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>::_::cls()k=ˇ\2%5memset(26624,204,5^5)n=0for i=1,86do v=ord(&quot;cS2C2Cr232$R$31b1CES2Kc32cKc2#2cK2S2c3K2C2c32KcC23nNfffff6%6565&amp;E&amp;%'575g'%ggggggggggg'&quot;,i)-35for j=0,v\16do sset(n%8,n\8,v)n+=1end z=i%2sspr(0,z*(k*2+6),8,6-z*4,60,65-k\3+z*6)spr(32,i*8-ˇ%8-8,73,1,2)end ˇ+=1flip()goto _</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <img style="" border=0 src="/media/42638/39_lemmings_tweet_1.gif" alt="" /> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>pal({-4,-5,15},1)::_::cls()for n=1,380do sset(n%6,n\6,(ord(&quot;+%K%C&amp;S2_$?$7(2_C%K.[2S$S$?$:(&amp;2K-K&amp;S2S$3&amp;S$?$S&amp;C-K&amp;S2S$3&amp;3V7/_#+%K%C&amp;S23&amp;307(2_C%K.[2S$S$3&amp;:(&amp;2K-K&amp;S2S$S$3$?$S&amp;C-K&amp;S2S$?$?T7/_&quot;,n\3+1)-35)\4^(n%3)%4) if(n&lt;7)spr((ˇ+n)\1%8*16,(ˇ+n*32)%176-8,60)end ˇ+=.5flip()goto _</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <img style="" border=0 src="/media/42638/pitfall_tweet_0.gif" alt="" /> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>k=139pal(5,k,1)o=ord::_::cls()for i=0,509do v=o(&quot;;C$);C$);C$);C#%+C#%;D$);C$2?D$)?L$2;C$2=S$1[S$A;C$);O(1;C&amp;A/^%//T'#ES&amp;,*&amp;#;737####/+&quot;,i\6+1)-35sset(i%40,i\40,o(&quot;4?;;333&quot;,v\2^(i%6)%2*(i+120)\80))rectfill(0,i*8,k,i*8+7,o('55555::9',i-3))end spr(ˇ\3%5,ˇ%k-8,64,1,2)ˇ+=1flip()goto _</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>People have shown interest in learning my techniques, so I decided to make this tutorial. The ideas here could come in handy for tweetcarts, entries for TweetTweetJam or the upcoming 1K Jam, or just fitting more into a regular cart.</p> <h2>Fitting sprite data into strings</h2> <p>In order to fit as much information as we can into a few characters, we'll need to use data strings with the largest possible range of of values, and therefore the largest range of symbols from Pico-8's character set.</p> <img style="" border=0 src="/media/42638/chrprinter_tutorial_0.png" alt="" /> <p>Here's a picture from my CHR Printer tool to illustrate (characters 0-15 aren't pictured, as P8SCII control codes and other factors make using them problematic). Of the remaining characters, 34, and 92 cause errors and are shown in red, those which count as two chars on Twitter are shown in blue, and those that count as one are in white. This means the optimum range is the span of 92 characters from 35 (#) to 126 (~). #92 (backslash) can cause errors, but there are a couple simple ways to deal with that which we'll get to later.</p> <p><strong>Important: if you're not actually going to post your code to Twitter, you can use all the characters that come after the white ones, giving you 221 to work with.</strong></p> <h3>Really important: Since the range of characters we'll be using includes those for Pico-8's Puny Font, Pico-8 must be put into &quot;puny font mode&quot; by pressing ctrl+p before pasting your data strings into it, and you must be in puny font mode when copying from Pico-8 to paste into a tweet. I've had errors pop up many times because I forgot to do this. o_O</h3> <p>Anyway, to turn our string into values, we'll use Pico-8's ord() function, and since the characters we can use start at #35, we'll need to correct for this by adding 35 when encoding. When decoding, we'll subtract 35, like this: <em>value = ord(string,character)-35</em></p> <p>Now we can find the maximum number of pixels that each string character can hold. So far I've used two different methods for encoding pixels into characters: directly encoding values for a number of pixels, or a simple version of Run Length Encoding (RLE). RLE is more efficient for sprites with lots of single-color spans, while direct encoding is better for more detailed graphics. For both types, there's a tradeoff between range of possible colors and how many pixels can be encoded within our character range. Here are some examples of different combinations:</p> <h3>Direct pixel encoding (92 possible characters)</h3> <p>6 2-color pixels (2^6=64)<br /> 3 4-color pixels (4^3=64)<br /> 2 9-color pixels (9^2=81)<br /> 1 16-color pixel (16^1=16)</p> <h3>Run-Length Encoding (92 possible characters)</h3> <p>1-46 2-color pixels (2x46=92)<br /> 1-23 4-color pixels (4x23=92)<br /> 1-10 9-color pixels (9x10=90)<br /> 1-5 16-color pixels (16x5=80)</p> <h2>Decoding strings</h2> <p>Once we've encoded our strings, we need to decode them back into images. The most straightforward method would use a few nested loops-- two outer loops for horizontal and vertical position, and an inner loop to iterate through the pixel values described by each character. It would take up a large chunk of a tweet just for these loops, though, so we've got to condense things. Fortunately, we can do this using math, specifically the floor divide () and modulo (%) operators. The modulo operator lets us loop through a specific range of values, while floor divison lets us switch to a new value at a specific point. Used together, these can have all kinds of non-linear effects and replace various conditional logic. Here, index_variable%width gives horizontal position and index_variable\width gives vertical position, and the operators are also used along with ^ to iterate through the color values in a single character for per-pixel decoding, or without to separate the color and run length values for an RLE decoder. In both cases, v equals the value derived from each character of the data string (represented by empty double quotes).</p> <p>Here's the basic setup for a direct pixel decoder, where:</p> <p><em>w = image width in pixels<br /> h = image height in pixels<br /> p = pixels stored per character<br /> c = possible colors per pixel</em></p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>for i=0,(w*h-1)do v=ord(&quot;&quot;,i\p+1)-35sset(i%w,i\w,v\c^(i%p)%c)end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>The expression <strong>v\c^(i%p)%c</strong>, which I guess I'll call the <strong>pixel value</strong>, is where v, the value of each character, is separated out into its component values for each pixel. For instance, if there are 4 possible colors, the value for each pixel will be found by separating out the multiples of 4^0, 4^1, and 4^2,-- AKA 1,4, and 16.</p> <p>And here's the basic setup for an RLE decoder, where:</p> <p><em>t = total characters in string<br /> w = image width in pixels<br /> c = possible colors per pixel</em></p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>k=0for i=1,t do v=ord(&quot;&quot;,i)-35for j=0,v\c do sset(k%w,k\w,v%c)k+=1end end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Here, the run length is stored as a multiple of the total number of possible colors, and the desired color is added to that value. At decoding, we get the pixel value <strong>v%c</strong> by using modulo, and the run length by using floor division. An inner 'for' loop then sets the specified number of pixels to the correct color.</p> <h2>Encoding sprites</h2> <p>Now we need some encoders to build strings for our decoders. The following two encoders work by scanning the top left portion of the spritesheet left to right and top to bottom, and storing either direct pixel values or color and length values in each character. They also automatically determine the largest color value present, and use that to determine how many pixels can be contained in each character. Since color 0 (transparent) is used, the total number of colors will be 1 greater than the highest color value, so if color 9 is used, there are 10 potential colors. If you aren't posting to Twitter and want to use the most possible characters, set the 'value_range' variable to 221.</p> <h3>Direct pixel encoder</h3> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>width=8 height=8 value_range=92 ----------- cls() top_color=0 for i=0,width*height-1 do col=sget(i%width,i\width) top_color=max(col,top_color) end colors=top_color+1 i=0 while colors^(i+1)&lt;value_range do i+=1 end pixels=i str=&quot;&quot; i=0 while i&lt;width*height do val=0 for j=0,pixels-1 do val+=sget((i+j)%width,(i+j)\width)*colors^j end if(val==57) val=-2 str..=chr(val+35) i+=pixels end printh(str,'@clip') ?'string pasted to clipboard',12,60,11 ?colors..' colors/pixel',36,68,13 ?pixels..' pixels/character',28,76 ?#str..' characters total',24,84,6</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>RLE encoder</h3> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>width=8 height=8 value_range=92 -------- cls() top_color=0 for i=0,width*height-1 do col=sget(i%width,i\width) top_color=max(top_color,col) end colors=top_color+1 max_span=value_range\colors s=&quot;&quot; local n,len=0,0 while n&lt;width*height do local val,nxtval=sget(n%width,n\width),sget((n+1)%width,(n+1)\width) if nxtval==val and len&lt;(max_span-1) then len+=1 else v=val+len*colors if(v==57) v=-2 s..=chr(v+35) len=0 end n+=1 end printh(s,'@clip') ?'string posted to clipboard',12,60,11 ?colors..' colors/pixel',36,68,13 ?max_span..' pixel max span length',18,76,13 ?#s..' characters total',24,84,6</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>Optimizing for encoding</h3> <p>To make the most of your data strings, you'll need to optimize your spritesheet. For example, here are the raw spritesheets for the demos I showed earlier (well, half of the frames for the Lemmings one).</p> <img style="" border=0 src="/media/42638/Jelpi_tweet_spritesheet_1.png" alt="" /> <p>-</p> <img style="" border=0 src="/media/42638/Lemmings_tweet_spritesheet.png" alt="" /> <p>-</p> <img style="" border=0 src="/media/42638/Pitfall_tweet_spritesheet.png" alt="" /> <p>While Jelpi uses colors from Pico-8's full palette and works better with RLE, the Lemmings and Pitfall Harry sprites only use 4 and 2 colors respectively, and both are stored more efficiently using direct pixel encoding. Because of the math involved, if you want to restrict your palette to save space, you'll need to draw the sprites using the lowest-numbered colors. With 4 colors we have to use colors 0-3, and with 2 colors, we use 0 and 1. It looks weird, but we'll fix it later.</p> <p>In addition to this, the Lemmings sprites aren't laid out horizontally as you might expect, but vertically. That's because they're only six pixels wide, so placing them side-by-side would take 1/3 more space than necessary. You'll also notice there aren't full frames of animation for Jelpi's run because there wasn't enough space. Because Jelpi's head is the same in each frame and only the position of the legs change, I just drew the head once and stored stacked animation frames of the 2-pixel high leg area, which will be displayed using the sspr(). Jelpi does move up and down a pixel during the run cycle, but that can be added at runtime by altering the vertical sprite position based on the current frame in the animation cycle using '%' and '\'. As you can see, even with efficient compression, it takes some thought and ingenuity to squeeze graphics down to a few hundred characters.</p> <p>Now let's deal with the problematic backslash character. If we left this in our strings we'd get an error at runtime, but the encoders above will look for the value corresponding to a backslash and replace it with the value for an exclamation point. This will cause some slight graphical glitches, but not an error. If, after encoding, you notice any exlamation points in your string or glitches in your sprites, you can either slightly rework them to avoid this, or use this bit of extra code.</p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>v-=v\▥*59</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Just place this right after &quot;-35&quot; in the decoder to intercept the incoming value, and if it's -2, it will be set to the correct value of 57. It basically does the same thing as &quot;if(v==-2)v=57&quot;, but in fewer characters.</p> <h2>Sprite recoloring</h2> <p>If you restricted your sprites' color range to save space, you'll need to set the pixels back to the right colors. There are a few different ways to do this, but each one takes around a dozen characters or more, so experiment and compare how many chars it takes to recolor vs. just encoding the right colors in the first place.</p> <img style="" border=0 src="/media/42638/tweet_sprite_recolor_0.png" alt="" /> <p>Using the Lemmings sprites above as an example of recoloring indexed color sprites, I've come up with two main solutions for this. The first lets you recolor using Pico-8's standard palette, as you can see in the second lemming , and the second lets you use any mixture of colors from the standard and dark palettes, which can be a better fit, as seen in the third lemming.</p> <p>The standard color method uses a small extra string placed inside an ord() function to act as a lookup table. Place this into the sset() command in the decoder as the third input, with the pixel value <em>v\4^(i%3)%4</em> used as the string position input. It will intercept the pixel values of 0-3 which the output of the string produces, and instead of using it to color the pixels directly, it will use the small string as a lookup table for the corrected colors. Note that you only need 1 less than the number of colors in your palette, (e.g. 3 string characters for a palette of 4 colors). This is because there's no entry 0 in the string, so color 0 will remain unchanged and transparent, which we'll want. As for which characters to use, you can use any which are in the correct column of the character set, though I like to use chars 48-63 as shown in green below, because it makes things more intuitive for 10 out of the 16 colors. Using these characters, our lookup string will have characters from columns 13,11, and 15, or <strong>&quot;=;?&quot;</strong>.</p> <img style="" border=0 src="/media/42638/Recoloring_chars.png" alt="" /> <p>-</p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>for i=0,380do v=ord(&quot;&quot;,i\3+1)-35 sset(i%6,i\6,ord(&quot;=;?&quot;,v\4^(i%3)%4))end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>For recoloring using mixed standard and dark palette colors, the most compact way I've found is to declare a small table at the beginning of your code instead of using a lookup string. Like the standard color method, this won't effect color 0, just keep in mind that colors 1-whatever will have those new values wherever they are used. The dark palette colors are usually called by their color palette index number+128, so for medium blue we'd add 12 (light blue) and 128 for 140. I've found that the same effect can be had by just subtracting 16, though, instead of adding 128, which can save a character. So, to get medium blue, we can use 12-16= -4. Likewise, for medium green, we can use 11-16= -5.</p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>pal({-4,-5,15},1)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>-<br /> Now, the Pitfall sprites are more of a special case. They're one bit, or just 0 and 1. This saves space and allows 6 on or off pixels per character for a 92-character range, or 7 if you're using a range of 221 characters, but by default everything will just be dark blue. I've come up with two main methods for dealing with this as well. The first is just setting every 'on' pixel to a certain consistent value, and the second recreates the look of Atari graphics by recoloring the 1-bit sprite on every scanline.</p> <img style="" border=0 src="/media/42638/tweet_sprite_recolor_1.png" alt="" /> <p>For a starting point, this is what the code looks like to paint Harry in basic dark blue.</p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>for i=0,509do v=ord(&quot;&quot;,i\6+1)-35 sset(i%40,i\40,v\2^(i%6)%2)end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Changing the sprite color from dark blue to a single other color is very simple. Since every non-transparent pixel equals 1, just multiply the pixel value <em>v\2^(i%6)%2</em> by the color you want. In the case above, I used 11.</p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>for i=0,509do v=ord(&quot;&quot;,i\6+1)-35 sset(i%40,i\40,v\2^(i%6)%2*11)end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>For colors that vary per scanline, we'll need another small lookup string, but this time we need more math. Specifically, we'll take the pixel value and multiply it another expression, (i+120)\80.</p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>for i=0,509do v=ord(&quot;&quot;,i\6+1)-35 sset(i%40,i\40,ord(&quot;4?;;333&quot;,v\2^(i%6)%2*(i+120)\80))</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>The 80 here represents twice the width of pixels of all our sprites, which are 40 pixels wide, and the floor divide means this value will count up by 1 every time i increases by 80, or every two rows, so each character in our lookup string will color two rows of pixels. If we replaced 80 with 40, we'd be able to color each individual row with a string entry. An offset (120) is added to i here because if not, no color would be assigned until two rows had been completed, cutting of Pitfall Harry's head (yikes...). The offset here is 120 specifically because that means the pixels will be set to the color of the first symbol only for the first row, (80 to get to 1, plus 40 so the value will change to 2 after 40 pixels, or 1 row), which is helpful because his brown hair is only one pixel tall.</p> <p><em>Note, these recoloring methods should also with an RLE decoder, just make sure you have the right number of entries in any lookup strings and plug the pixel value of the decoder into them or multiply it by a constant in a similar way. The one possible exception is the per-scanline color trick, haven't tried that with RLE yet.</em></p> <h2>Animating Sprites</h2> <p>Finally, here are a few tips for animating your sprites in very few characters. First of all, since your animations will probably be just looping a set pattern of sprites, we can do that easily with the modulo operator (%). You just need a variable that increases or decreases every frame, what multiple of this you want the animation to update at, and a modulo value to make it loop through a certain number of animation frames. Here are a couple different ways of doing this for the Lemmings sprites, using this expression as the sprite number input in the spr() function.</p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>t()\.1%8*16 </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>The variable t() is an abbreviation for time(), and will give the time elapsed since startup. By itself this counts up tiny fractions of an integer every frame, and by 1 each second. Neither of these are useful, but floor dividing it by .1 means that the the animation frame will increase by the integer 1 every 10th of a second, or every 3 frames, which is a good speed. This expression is then run through %8, resulting in a sprite number that cycles from 0-7, but then we also need to multiply that by 16 to match up with the right sprite numbers (0,16,32,...), because the sprites are laid out vertically.</p> <p>Since it might be a good illustration and starting point, here's a complete, simplified version of the Lemmings tweet.</p> <img style="" border=0 src="/media/42638/46_tutorial_tweet_2_0.gif" alt="" /> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>pal({-4,-5,15},1)for n=0,380do v=ord(&quot;+%K%C&amp;S2_$?$7(2_C%K.[2S$_$?$:(&amp;2K-K&amp;S2S$S$3&amp;?$S&amp;C-K&amp;S2S$3&amp;3V7/_#+%K%C&amp;S23&amp;307(2_C%K.[2S$S$3&amp;:(&amp;2K-K&amp;S2S$S$3$?$S&amp;C-K&amp;S2S$?$?T7/_&quot;,n\3+1)-35sset(n%6,n\6,v\4^(n%3)%4)end poke(24364,3)::_::cls()spr(t()\.1%8*16,29,28)flip()goto _</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>-</p> <p>Next up, here's how the animation code works for Pitfall Harry.</p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>spr(ˇ\3%5,ˇ%k-8,64,1,2)ˇ+=1 </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Here, we use a similar structure, an iterating variable run through a floor divide to determine animation speed, then a modulo to control the number of animation frames. Here, we have a new sprite used every 3 frames, and the there are 5 sprites in the run cycle. The same iterating variable is also used in the x-position, along with a modulo. 'k' here is an alias for the constant value of 139, and combined with a modulo, this is what makes Harry's horizontal position loop. The expression <strong>ˇ%k</strong>, which means <strong>position%139-8</strong> makes his horizontal position cycle from -8 to 130, so he keeps running by.</p> <p>As a side note, the symbol I'm using there, <strong>ˇ</strong>, is character 149 from Pico-8's character set, which you get by pressing shift+v, and looks like two little birds. I'm using it because it has some unique properties. It only counts as one byte in a tweet, but like the other glyphs, it has a default value before being assigned one (in this case it's -2624.5). Because we're running it through a modulo, it's exact value doesn't matter, so we don't need to intialize it, which saves 3 characters =).</p> <p>Finally, Here's the code for displaying the Jelpi demo sprites, showing some more advanced tricks.</p> <div> <div style="max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>k=ˇ\2%5... z=i%2sspr(0,z*(k*2+6),8,6-z*4,60,65-k\3+z*6)spr(32,i*8-ˇ%8-8,73,1,2)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>To start, k gives the animation speed (new sprite every 2 frames) and cycle length (5 sprites). With the variable z here I create a value that cycles between 0 (for the head), and 1 (for the body). This is for two reasons. First, it lets me co-opt the same loop that I'm using to set the sprite pixels, to also display all the sprites. The single sspr() function here draws, by turn, both the head and body. The horizontal spritesheet position starting is always the same, but vertical is adjusted by the z variable to be either 0, or cycle through the values 6,8,10,12,and 14. z is also used to cycle sprite height between 6 pixels for the head and 2 pixels for the legs. For vertical position, this is increased by 6 for the legs to put them just below the head, but both head and legs are also modified by <strong>-k\3</strong>. This is what creates the up and down motion for the animation. Because k cycles between 0 and 4, the value will be -1 when k is &gt;=3, so Jelpi will go up one pixel for the 4th and 5th animation frames. (It's so simple o_O).</p> <p>The spr() function draws the ground layer by setting several sprites side by side at 8-pixel increments, and it creates the looping effect by performing a, you guessed it, modulo operation on the position variable, represented by our bird glyph, while 8 is also subtracted to make sure there's never a blank space on the left edge of the screen.</p> <hr /> <p>Wow, we got pretty far in the weeds there, didn't we? Hopefully you were able to get through that and it made sense. Anyway, that's all for now. I hope that gave you some good tools and ideas, and that you have fun squeezing graphics down to an absurdly small size. I look forward to seeing what you come up with. =)</p> https://www.lexaloffle.com/bbs/?tid=44375 https://www.lexaloffle.com/bbs/?tid=44375 Wed, 25 Aug 2021 06:20:22 UTC Floppy Bird (Tweet Tweet Jam 6) <p> <table><tr><td> <a href="/bbs/?pid=94162#p"> <img src="/bbs/thumbs/pico8_floppy_bird-4.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=94162#p"> floppy_bird</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=94162#p"> [Click to Play]</a> </td></tr></table> </p> <p>This is a clone of the infamous Flappy Bird, complete with detailed graphics and 60fps gameplay, and scrunched down to under 560 characters. Any feedback about the controls or physics is welcome.</p> <p>Controls: Press any button to fly upward/restart</p> <p><strong>Version 1.1</strong> - Added high score and a brief pause before gravity kicks in on restart.</p> https://www.lexaloffle.com/bbs/?tid=43572 https://www.lexaloffle.com/bbs/?tid=43572 Mon, 28 Jun 2021 01:10:42 UTC Super Mario Bros. World 1-1 in 3 Tweets <p> <table><tr><td> <a href="/bbs/?pid=93586#p"> <img src="/bbs/thumbs/pico8_tjlr01-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=93586#p"> Tweet Level Renderer</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=93586#p"> [Click to Play]</a> </td></tr></table> </p> <img style="" border=0 src="/media/42638/W1-1.gif" alt="" /> <p>This is the full level 1-1 from Super Mario Bros., graphics and all, contained in 3 tweets, or 840 bytes. The first 280 bytes contains the actual level data (including level-specific graphics), and the next two contain the rendering engine and the main tileset. I'm thinking about making some more add-on levels, which should each fit in a single tweet.</p> <p>I've made a number of Tweetcarts, and also worked on systems to efficiently compress graphics and level data to fit more on a PICO-8 cart, so this is kind of the logical combination of both. I'm not going to try and fit a full NES game in tweet form, but it's a neat demo that helped me learn some new optimization strategies.</p> https://www.lexaloffle.com/bbs/?tid=43354 https://www.lexaloffle.com/bbs/?tid=43354 Wed, 16 Jun 2021 02:30:17 UTC PicoMap: Large game worlds made easy <p> <table><tr><td> <a href="/bbs/?pid=91798#p"> <img src="/bbs/thumbs/pico8_pm_contra_demo-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=91798#p"> pm_contra_demo</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=91798#p"> [Click to Play]</a> </td></tr></table> </p> <p> <table><tr><td> <a href="/bbs/?pid=91798#p"> <img src="/bbs/thumbs/pico8_pm_smw_demo-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=91798#p"> pm_smw_demo</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=91798#p"> [Click to Play]</a> </td></tr></table> </p> <p> <table><tr><td> <a href="/bbs/?pid=91798#p"> <img src="/bbs/thumbs/pico8_picomap-13.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=91798#p"> picomap</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=91798#p"> [Click to Play]</a> </td></tr></table> </p> <p> <table><tr><td> <a href="/bbs/?pid=91798#p"> <img src="/bbs/thumbs/pico8_pm_testcart-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=91798#p"> pm_testcart</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=91798#p"> [Click to Play]</a> </td></tr></table> </p> <p>Current version: 0.41</p> <p><strong>Changelog</strong></p> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"><br /> <strong>Version 0.41:</strong> </p> <ul> <li>Added object pre-processing &amp; caching system for much lower CPU usage in levels w/many objects</li> <li>Added easy importing of spritesheet and map memory from external cart</li> <li>Removed option to export individual level maps</li> <li>Added customized grid color scheme for each background color</li> <li>Added camera position bookmark for each level map</li> <li>Object bounding box now appears along with X symbol to indicate objects can be deleted</li> <li>Added readout of number of objects within screen range and total number in map (toggles on/off w/ 'o' key).</li> <li>Spritesheet transparency color can now be set in system settings</li> <li>Changed name of &quot;Eraser Objects&quot; to &quot;Mask Objects&quot; to avoid user confusion</li> <li>Fixed: Undefined objects can no longer be accidentally added to level string.</li> <li>Various optimizations</li> </ul> <p><strong>Version 0.40:</strong></p> <ul> <li>Added autotile mapping type</li> <li>Added dedicated eraser object type</li> <li>Eraser tiles now have visible symbol in editor for improved usability</li> <li>Added per-object autotile interaction setting</li> <li>Added outline of current default object selection to define object screen</li> <li>Changed button for toggling define object screen from 'z' key to spacebar</li> <li>Editor now accurately previews just a single layer of tiles</li> <li>Reduced CPU usage for editor when rendering large levels</li> <li>Reduced CPU usage for external cart rendering functions</li> <li>Reduced token count for alternate external cart functions without autotiling feature</li> <li>Level arrays now stored in extended RAM instead of table, reducing Lua memory usage</li> <li>Added drop-in substitute function for fget(mget), for use of existing tile collision routines with level arrays</li> <li>Updated sprite and map memory decompression function allows use of all 256 tiles</li> <li>Map_array function now gets size parameters directly from level arrays</li> <li>Fixed: exporting sequential levels no longer causes data location errors</li> <li>Fixed: objects extending beyond bounds of level array no longer cause runtime errors</li> </ul> <p><strong>Version 0.30:</strong></p> <ul> <li>Added support for parallax scrolling backgrounds</li> <li>Added eraser tile functionality</li> <li>Made repeating pattern size adjustable for bordered objects</li> <li>Revamped object definition structure to handle future added features</li> <li>Sprite flags now export along with sprite and map data</li> <li>Various optimizations and bug fixes</li> </ul> <p><strong>Version 0.21:</strong></p> <ul> <li>Updated appearance and changed system name from <em>Metatile Map Editor</em> to <em>PicoMap</em></li> <li>Various bug fixes</li> </ul> <p><strong>Version 0.20:</strong></p> <ul> <li>Reconfigured repeating pattern object types for adjustable pattern size</li> <li>Added level delete option to editor menu</li> <li>Changed various design elements to improve usability</li> </ul> <p></div></div></div></p> <hr /> <p>PicoMap is a graphical map editor that allows console game size worlds to fit in a single Pico-8 cart. It does this using a version of metatiles, a compression technique used commonly with retro systems like the NES. Basically, levels are stored more efficiently by being built from groups of tiles instead of single tiles, just as images are stored more efficiently when built from tiles rather than single pixels.</p> <p>To give you an idea of how efficient the scheme is, I built a playable demo of all Super Mario Bros. level maps with an earlier version, and they fit in just 3.9KB, about 1/3 of the storage space the system makes available. <a href="https://www.lexaloffle.com/bbs/?tid=39469">https://www.lexaloffle.com/bbs/?tid=39469</a></p> <p>I've worked hard to minimize the system's footprint so it's easy to add to game carts. It's very CPU-efficient and well-suited to 60fps games since map decompression is done only when loading a new level, and the system functions take up about 10% of the compressed size and token limits, or about 7% for the version without autotiling. You can use PicoMap in any project you want and modify the code to fit your application, just be sure to include attribution somewhere in your cart. Hopefully this will enable people to make some cool larger-scale games like platformers and action-adventures.</p> <hr /> <p><strong>System outline</strong></p> <p>PicoMap lets users create levels from metatile &quot;objects&quot;, like a tree, a hill, or a building, each of which is basically a reference to a rectangle of tiles in Pico 8's map memory. In the editor, levels are stored as lists of objects in string form, but when exported to a separate cart, these are condensed into raw binary data and stored in its spritesheet and map areas. Meanwhile, four strings are output to the clipboard-- compressed sprite and map memory data strings, and string-based lookup tables for the location of each level's data in cart memory, and a list of defined autotile types.</p> <p>When the destination cart is run, the binary level data is transferred to Pico-8's upper 32KB of RAM and the spritesheet and map memory data are decompressed to the proper areas in cart memory. On loading a level, a table is created to contain its map, and this is filled using instructions from the binary level data. A portion of the level table slightly larger than one screen is then copied to the top right corner of Pico-8's map memory once each frame and mapped to the display. This allows each level to be any width or height, and up to 256 screens in size.</p> <hr /> <p><strong>Controls</strong></p> <p><strong><em>Editor</em></strong></p> <p>Left Mouse btn.-----Select or place object/bring up object placement preview<br /> Right Mouse btn.---Click and hold to drag view/turn off object placement preview<br /> Directional keys----Move view<br /> Scroll Wheel--------Select object number<br /> Space---------------Toggle object editor screen<br /> Z--------------------Undo<br /> X--------------------Delete object when mousing over its top left corner w/object placement preview off (dotted outline and X symbol will appear)</p> <p><strong><em>Editor shortcut keys</em></strong></p> <p>V-------------------Change variance type (when in obj.editing mode)<br /> M-------------------Change mapping pattern type (when in obj.editing mode)<br /> G-------------------Toggle grid<br /> S-------------------New level string submenu<br /> Q-------------------Previous level map<br /> W------------------Next level map<br /> I--------------------Import spritesheet, sprite flags, and map memory from external cart<br /> E-------------------Export level maps to external cart<br /> H-------------------Reset current map to Home position (screen 1,1)<br /> O-------------------Toggle display of # of objects in range of screen and total # of objects in map</p> <p><strong><em>Viewer Cartridge</em></strong></p> <p>Directional keys---Move camera<br /> Z-------------------Previous level map (optional)<br /> X-------------------Next level map (optional)</p> <hr /> <p><strong>Using the editor</strong></p> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <p><strong>Creating and managing maps</strong></p> <p>Once you've started the editor, if you want to create a new map string, open the menu and click on the 'new level map' icon. Select your desired background color, level type (work in progress), level width and height in screens, and press the checkmark icon to create the new string. To delete a map, go to it using either the left and right arrows in the menu, or the q and w keys on your keyboard. Then click the &quot;delete&quot; icon. If you delete a map by mistake, just click the Undo icon.</p> <img style="" border=0 src="/media/42638/picomap_managing_maps.gif" alt="" /> <p><strong>Spritesheet</strong></p> <p>Despite greatly increasing map storage size, PicoMap lets you use the entire spritesheet for graphics. It does this by compressing the spritesheet to a string, and storing levels as binary data (or what looks like rainbow-colored noise) in the spritesheet and map memory. Just draw or paste all of your sprites into the spritesheet of the editor cart, and when you export your levels to a test cart or your game cart, the spritesheet will be exported as well.</p> <p><strong>Defining Objects</strong></p> <p>Since your maps will be made from tile patterns in Pico-8's map memory, you'll need to draw each object type there before you can use it. Due to the way the system works, you'll need to keep a few limitations in mind.</p> <img style="" border=0 src="/media/42638/77_picomap_illustrations_0.png" alt="" /> <p>To create objects to populate your level map, select the object category using the icons on the top right, then the object number either by clicking the thumbnail and selecting a slot from the drop down menu, or by using the mouse wheel. To create an object, go into object editing mode by pressing the spacebar and outline the rectangle of tiles that defines the object's default (minimum) size.</p> <img style="" border=0 src="/media/42638/picomap_define_objects.gif" alt="" /> <p><strong>Object settings</strong></p> <p>Each object type can be made to have a variable size. This is done by changing two properties, its variance and mapping pattern. These are represented by icons in the lower right corner of the editor, next to the object's ID number.</p> <p>--Variance<br /> An object's variance type tells how its size can be altered when it's being placed in a level.The box that's highlighted defines the default (or minimum) size of an object, but if its size is variable along an axis, the size can be extended up to 15 tiles larger than the default size in that direction. Fixed-size objects take 2 bytes each, while ones whose size can vary on one or two axes take 2.5 and 3 bytes respectively (though it'd be possible to limit this to 2.5 bytes max via lookup tables if you only need certain types in a given level).</p> <img style="" border=0 src="/media/42638/picomap_illustrations_1.png" alt="" /> <p>--Mapping type<br /> This property controls the pattern of how map tiles are used to build objects in your level. This can allow objects to be made much larger than their representations in Pico-8's map area while still keeping their intended traits.</p> <img style="" border=0 src="/media/42638/picomap_illustrations_2.png" alt="" /> <img style="" border=0 src="/media/42638/53_picomap_illustrations_3.png" alt="" /> <img style="" border=0 src="/media/42638/picomap_illustrations_4.png" alt="" /> <img style="" border=0 src="/media/42638/picomap_illustrations_5.png" alt="" /> <img style="" border=0 src="/media/42638/picomap_illustrations_6a.png" alt="" /> <img style="" border=0 src="/media/42638/80_picomap_illustrations_6b.png" alt="" /> <p><strong>Something to keep in mind about autotile objects is once an item is set to this object type, the top left corner tile in its pattern (the basetile) is automatically processed as an autotile wherever it appears in the map. This can be useful for things like making screen-filling autotile objects by creating a multi-screen object using the basetile as the pattern. When exporting a level, the system outputs a list of all active basetiles in a string-based table called at_list, though if you're using the system functions without autotiling in your cart, you can just delete this.</strong></p> <p>To change settings for an object, go into the object editing mode and click on the buttons in the bottom right hand corner, or press the 'V' key to cycle through variance types and the 'M' key to cycle through mapping types.</p> <img style="" border=0 src="/media/42638/82_picomap_object_settings.gif" alt="" /> <p><strong>Mask Tiles</strong></p> <p>To increase versatility and save memory, it's possible to partially mask off objects and let the background show through. This can be done by using the dedicated mask tool in the menu, or by building objects partially or fully from sprite #1, which contains an 'x' icon. This x pattern will appear in the editor, but will be invisible when you export your level data to a viewer cart.</p> <img style="" border=0 src="/media/42638/86_picomap_illustrations_6c.png" alt="" /> <p><strong>Building maps</strong></p> <p>Now that you've created a level string and defined some objects, you can start building level maps. Before you do, though, here's something to keep in mind. While the editor will display your objects exactly how you place them, when they're exported and compressed, they'll be drawn in order of the screen in which they start.</p> <img style="" border=0 src="/media/42638/picomap_illustrations_7.png" alt="" /> <p>Now using either the arrow keys or clicking and dragging with the right mouse button, you can move your view around the level to place objects. Pressing the right mouse button will also turn off the placement preview for the selected object, but you can turn that back on by clicking the left mouse button. Now, select the object you want to place, and either left click (for a fixed-size object), or left click and drag to place objects in the level. If you make a mistake, you can either click the undo icon, or you can delete any object by mousing over its top left corner until a white &quot;X&quot; appears, and pressing the X button. To toggle a grid that makes it easier to judge position and screen boundaries, just click the second icon at the top of the screen, or press &quot;G&quot;.</p> <p>Foreground layer</p> <img style="" border=0 src="/media/42638/picomap_building_maps.gif" alt="" /> <p>Background layer</p> <img style="" border=0 src="/media/42638/71_picomap_building_maps_2.gif" alt="" /> <p><strong>Saving maps</strong></p> <p>The system continuously outputs the level you're working on and the list of definitions for all the objects you've defined to the clipboard as strings. To save these to the cartridge, just quit PicoMap by pressing escape, paste the contents of the clipboard over the current strings in the &quot;data strings&quot; tab of Pico-8's code editor, and save the cart.</p> <img style="" border=0 src="/media/42638/66_picomap_saving_maps.gif" alt="" /> <p><strong>Exporting Maps</strong></p> <p>Once you've made some levels, you can export them to a separate viewer cartridge, suitable to be incorporated into a game. The pm_testcart cartridge above is the default way to do this. It contains example code for viewing your maps, along with two different versions of the PicoMap system functions-- one with autotiling, and one without that uses fewer tokens. You can pick which one you want to use by commenting out or deleting the other set. To use the cart, download it and save it as a .p8 file. You can rename it to whatever you want, just make sure the name matches the &quot;export_cart&quot; variable in the editor cart's _init() function so your data will export to the right place.</p> <p>Now run the editor, open the menu, and click the bottom cartridge icon to export all level maps to your new cart. Open the viewer cart, make sure &quot;puny font mode&quot; is enabled by pressing ctrl+p, and paste the contents of the clipboard into it. Now you can view your levels.</p> <img style="" border=0 src="/media/42638/picomap_exporting_levels.gif" alt="" /> <p><strong>Importing Assets</strong></p> <p>To make transferring assets into the editor cart easier, the editor lets you import the spritesheet, sprite flags, and map memory from an external cart, which is useful if you've made a spritesheet previously, or you're switching over to a new version of PicoMap. Just set the &quot;import_cart&quot; variable in the first tab of the editor to the name of the cart you want to import the assets from, then run the editor and click the cartridge icon with the arrow going towards it to import. Note: sprite #1 will automatically be overwritten with the 'x' mask object sprite, also, make sure the map memory in the cart either has reference object patterns instead of regular level maps, or is empty.</p> <p><strong>Layering Maps</strong></p> <p>PicoMap uses a special function called map_array() to copy tile data from each layer to a dedicated section of map memory and then display it on screen. To display multiple maps on top of each other, which can increase scene complexity and/or be used for parallax scrolling, you need to first initialize one empty array per layer when initially loading the level, then make the proper number of map_array() calls per frame to draw each layer, as follows.</p> <p>.<br /> <strong>map_array(array,scroll_coef,x_offset,y_offset)</strong><br /> .</p> <p><strong><em>array:</em></strong> this is the name of the array, such as &quot;bg&quot; for background or &quot;fg&quot; for foreground.</p> <p><strong><em>scroll_coef:</em></strong> This sets the relative scrolling speed of each layer by taking the camera movement commands from your code and multiplying it by the given value for each layer, which is useful for parallax scrolling effects. I recommend using a value of 1 for the foreground and a simple fraction like .25 or .5 for background layers. Layers in front of the foreground would use a value greater than 1.</p> <p><strong><em>x_offset and y-offset</em></strong> These set the starting offset of the map in tiles when it's first loaded. They're useful for things like positioning a background layer so that it starts at the right place in the repeating pattern.</p> <p>In addition to these inputs, width and height of each map are determined automatically, and background maps smaller than the foreground will automatically repeat once the level has scrolled far enough, so background layers can be much smaller than the foreground to save memory.</p> <img style="" border=0 src="/media/42638/picomap_level_demo.gif" alt="" /> <p><strong>Map Collision</strong></p> <p>Although PicoMap doesn't store levels in Pico-8's map memory, it still works with standard tile flags for map collision via use of a small function, afget(), which is included in the test cart and gets flag data directly from the level array. To use it, just replace fget(mget) with afget() in your existing map collision routines, making sure that the array name in the function matches the name you're using for your foreground array.</p> <p><strong>Entity Objects (Work-in-progress)</strong></p> <p>By default, background, foreground, and entity object types all function the same way, with the categories merely helping organize map objects. If you uncomment a line of code in the build_array() function used in the viewer cart, no entity objects will be drawn to the map. This is to enable easy and storage-efficient enemy and NPC placement in levels by letting the user add an entry for each entity to a table using data from the entity object like ID number and x and y location for their spawn position in the level.</p> <img style="" border=0 src="/media/42638/83_picomap_entity_object_code.png" alt="" /> <p><strong>System Settings</strong></p> <p>The system has a number of settings that can be adjusted by changing values in the _init() function in the first tab of the editor cart.</p> <p><strong><em>obcounts:</em></strong> This table of 3 values controls the number of background, foreground, and entity object slots. There can be up to 236 object types in total, and up to 96 objects in a category can currently be displayed on screen at once. Keep in mind that each entry takes 7 hex digits, so if you want to increase the number, you'll need to add an appropriate number of zeros to the object definition string.</p> <p><strong><em>obtype:</em></strong> This sets the default object type that's selected when you start the editor. 1 for background objects, 2 for foreground objects, and 3 for entity objects</p> <p><strong><em>grid:</em></strong> Set this to 1 to have the grid turned on by default, or -1 to have it turned off</p> <p><strong><em>screen_ht:</em></strong> This value determines how many tiles high a single map screen is. Useful if you want to, for instance, demake an NES game with 15-tile tall screens, or an SNES game with 14-tile tall screens.</p> <p><strong><em>bord_obj_ptrn_size:</em></strong> This sets the size of the repeating pattern for bordered objects. Default is 1, but setting it to a larger number can allow larger repeating patterns.</p> <p><strong><em>import_cart:</em></strong> This is the name of the cart that the system will import the spritesheet,sprite flags, and map memory data from. Make sure this matches the name of the cart you want to import data from.</p> <p><strong><em>export_cart:</em></strong> This is the name of the cart that the system will export your levels to. It's a variable so that the user can create different editor carts for different projects that export to different viewer carts. Just make sure this matches the name of the cart you want to export the current maps to.</p> <p><strong><em>spritesheet_w:</em></strong> Sets the width (in pixels) of the area of spritesheet to be encoded to a string.<br /> <strong><em>spritesheet_h:</em></strong> Sets the height (in pixels) of the area of spritesheet to be encoded to a string.</p> <p><strong><em>transparency_col:</em></strong> Sets which color is treated as transparent by the spritesheet.</p> <p></div></div></div></p> <hr /> <p>As this is an early version of PicoMap, it's not yet complete and there are going to be bugs here and there. Please let me know about any issues you encounter, as well as any improvements you might think of. Thanks.</p> https://www.lexaloffle.com/bbs/?tid=42848 https://www.lexaloffle.com/bbs/?tid=42848 Tue, 11 May 2021 17:48:42 UTC Request: Allow pasting Puny font characters without entering Puny font mode <p>I've lately been working with image compression routines which output strings, and I've been able to increase efficiency and versatility by using as wide a range of characters to store data as possible. I've grown a bit frustrated at times, though, because any strings which use Puny font characters don't show up correctly unless I go into Puny Font mode. There have been a number of times I couldn't get things to work, and spent time debugging my code, only to realize that the problem was that I forgot to press Ctrl+P.</p> <p>I know it's not a big issue, and it can be worked around, but it's kind of inconsistent how strings can be created with Puny characters at any time, but you have to enter a certain mode to view the output correctly. Is it possible this could be changed without breaking some other feature? I mean, is there any way to let the program know that a string has been created by Pico-8 and therefore display it as intended?</p> https://www.lexaloffle.com/bbs/?tid=42236 https://www.lexaloffle.com/bbs/?tid=42236 Tue, 30 Mar 2021 17:16:39 UTC CHR Printer <p> <table><tr><td> <a href="/bbs/?pid=84578#p"> <img src="/bbs/thumbs/pico8_chrprinter-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=84578#p"> chrprinter</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=84578#p"> [Click to Play]</a> </td></tr></table> </p> <p><strong>Version 1.1 -- added ability to clear output string by pressing Z</strong><br /> <strong>Version 1.2 -- Removed characters 0-15 as Pico-8 0.2.2 made these unusable and problematic, tweaked appearance</strong></p> <p>This is a little tool for easily selecting and outputting characters from Pico 8's extended character set. You can build strings of any length and paste them as text using Ctrl+V or a right mouse click (on the BBS, you have to press Ctrl+C as well). I had some fun making it look like a version of the Pico-8 code editor with text characters as the icons, and since it's a pretty small program, I squeezed it down to fit in a tweet =).</p> <p>Controls:</p> <p>Arrow buttons---Select character<br /> X button--------Add character to output string<br /> Z button--------Clear output string</p> https://www.lexaloffle.com/bbs/?tid=40525 https://www.lexaloffle.com/bbs/?tid=40525 Sat, 21 Nov 2020 21:19:23 UTC Tweet Invaders DX: Space Invaders in 560 characters <p> <table><tr><td> <a href="/bbs/?pid=84441#p"> <img src="/bbs/thumbs/pico8_tidx-7.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=84441#p"> tidx</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=84441#p"> [Click to Play]</a> </td></tr></table> </p> <p><strong>Version 1.1 - Reworked code and squeezed in invader animation</strong><br /> <strong>Version 1.2 - Invader shots now target player, tweaked barriers, fixed disappearing score glitch</strong></p> <p>This is a port of the original arcade Space Invaders in just over half a kilobyte. I had to simplify a few things, like only having one invader type, cutting the UFO, and rethinking the lives system a little bit, but most of the major elements are there, and I think it plays fairly well.</p> <p>Features:</p> <ul> <li>Score system</li> <li>Level Progression</li> <li>Enemy Animation</li> <li>Enemy Fire with homing shots</li> <li>Destructible barriers</li> <li>Increasing invader speed and fire rate as number destroyed increases</li> <li>Damage system (your laser cannon changes colors after taking a hit, and will be destroyed after 3)</li> </ul> <p>Controls:</p> <p>L/R----Move laser cannon<br /> Z/X----Fire</p> https://www.lexaloffle.com/bbs/?tid=40468 https://www.lexaloffle.com/bbs/?tid=40468 Wed, 18 Nov 2020 18:23:18 UTC Tweet Invaders (TweetTweetJam, 278 Characters) <p> <table><tr><td> <a href="/bbs/?pid=83933#p"> <img src="/bbs/thumbs/pico8_ti-4.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=83933#p"> ti</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=83933#p"> [Click to Play]</a> </td></tr></table> <br /> This is something I've been working on here and there for a while, but the jam was finally an excuse to finish it. It's a simple space invaders clone in a single tweet. There's no enemy fire or shields, but the invaders do speed up as you destroy more of them, and it's tricky to take the last few out.;)</p> <p><strong>Version 1.1 - new enemy wave begins when current wave is destroyed</strong></p> https://www.lexaloffle.com/bbs/?tid=40276 https://www.lexaloffle.com/bbs/?tid=40276 Sat, 07 Nov 2020 17:10:29 UTC TweetPaint <p> <table><tr><td> <a href="/bbs/?pid=83565#p"> <img src="/bbs/thumbs/pico8_tweetpaint1_0-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=83565#p"> tweetpaint1_1</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=83565#p"> [Click to Play]</a> </td></tr></table> </p> <p><strong>Version 1.1 -- Updated for TweetTweetJam5, added square brush feature and tweaked appearance</strong></p> <p>This is a fully-functional painting program with image save capability in just 277 characters. It's built entirely around mouse controls, and requires a mouse with a scroll wheel for full functionality. Hope somebody enjoys playing around with it.</p> <p>Controls:</p> <p>Left mouse button ---- Paint<br /> Right mouse button --- Select paint color (hold and move mouse L/R)<br /> Middle Mouse button -- Save drawing to cart<br /> Scroll wheel --------- Change paintbrush size<br /> X key (hold) --------- Use square brush</p> https://www.lexaloffle.com/bbs/?tid=40107 https://www.lexaloffle.com/bbs/?tid=40107 Sun, 01 Nov 2020 05:58:44 UTC Sprite Recoloring and Animation System <p> <table><tr><td> <a href="/bbs/?pid=83432#p"> <img src="/bbs/thumbs/pico8_srs1_0-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=83432#p"> srs1_0</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=83432#p"> [Click to Play]</a> </td></tr></table> </p> <p>This is a compact and efficient system that enables recoloring of sprite graphics. For just 76 tokens, plus any function calls and data strings, you can easily change the color palettes of level tiles or characters based on an arbitrary number of conditions, and even animate them using color cycling. These kinds of techniques were used extensively in games for consoles like the NES to increase graphical variety while conserving memory space. The cart explains the syntax of the data strings and includes a simple demo of what the system can do.</p> https://www.lexaloffle.com/bbs/?tid=40062 https://www.lexaloffle.com/bbs/?tid=40062 Wed, 28 Oct 2020 18:14:42 UTC Analog Clock Tweetcart <p> <table><tr><td> <a href="/bbs/?pid=83389#p"> <img src="/bbs/thumbs/pico8_atc1-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=83389#p"> atc1</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=83389#p"> [Click to Play]</a> </td></tr></table> <br /> This is a little exercise in code optimization, a fully-functional analog clock in just 196 characters. I'm thinking I might add some features in the future while still keeping it under the 280-character limit, but for now it works just fine.</p> https://www.lexaloffle.com/bbs/?tid=40056 https://www.lexaloffle.com/bbs/?tid=40056 Tue, 27 Oct 2020 01:03:43 UTC Metatile System Demo: All Super Mario Bros. Levels in 4KB <p> <table><tr><td> <a href="/bbs/?pid=81450#p"> <img src="/bbs/thumbs/pico8_msd1_0-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=81450#p"> msd1_0</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=81450#p"> [Click to Play]</a> </td></tr></table> </p> <p>This is a demo for a system I developed that can store console-size game worlds on a Pico-8 cart. It contains every level and sublevel map from the original Super Mario Bros., and you can run and jump through all of them. There's no level progression, sound, or things to interact with besides platforms, but the map data includes placeholders for enemy spawn points and interactive objects, just uncomment a line in the init() function to make these visible. I'm not planning to release the finished game, as I don't want it to be taken down for copyright reasons, but I wanted to show some definitive proof that large-scale games are possible on Pico-8.</p> <p>Controls:</p> <p>L/R-------Move<br /> Z---------Run<br /> X---------Jump<br /> Up/down---Select Level<br /> Up+down---Restart Level</p> <p>Cart Technical Info:</p> <ul> <li>Uses standard map() and sprite flag functions (w/special camera offsets)</li> <li>60fps w/ CPU usage between 11-25%</li> <li>1352 tokens, including streamlined platformer engine and graphics/map decompression systems</li> <li>35% of compressed storage limit</li> <li>All maps contained in 3.9KB of binary data in map area</li> <li>No data stored in the 8KB spritesheet</li> </ul> <p>The big thing is the last point on the list. Instead of storing the nearly-incompressible level data in strings, I instead store the spritesheet in a string because it compresses over 2:1, leaving a full 12KB of potential level storage space. This means that while leaving the majority of compressed data capacity and token count unused, <strong>3 copies of every Super Mario Bros. level could fit on the cart!</strong> By my estimates, the worlds of mid-sized NES games like Megaman, Ninja Gaiden, Castlevania, Zelda, and even SMB2 (or original ones with similar scope and detail) could fit within that 12KB limit with some optimization. For instance, I estimate that Zelda 1's whole world could fit in around 6KB.</p> <p>This demo was built entirely in Pico-8, using a graphical level editor that I plan to release in the near future. It's currently a bit limited due to being tailored to the quirks and limitations of SMB, but I intend to add some more features and make it easy for anyone to build their own large game worlds using a simple point-and-click interface. Here's a little preview =).</p> <img style="" border=0 src="/media/42638/mme_proto.gif" alt="" /> https://www.lexaloffle.com/bbs/?tid=39469 https://www.lexaloffle.com/bbs/?tid=39469 Wed, 02 Sep 2020 18:44:49 UTC String-Based Logo &amp; Graphics Rendering System <p> <table><tr><td> <a href="/bbs/?pid=79345#p"> <img src="/bbs/thumbs/pico8_lgrs2_0-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=79345#p"> lgrs2_0</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=79345#p"> [Click to Play]</a> </td></tr></table> <br /> <table><tr><td> <a href="/bbs/?pid=79345#p"> <img src="/bbs/thumbs/pico8_lrs_1_0-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=79345#p"> lrs_1_0</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=79345#p"> [Click to Play]</a> </td></tr></table> </p> <p><strong>Version 2.0 --New system with much-improved ease-of-use and flexibility in one tiny, 74-token function!</strong></p> <p>With Pico-8's small spritesheet, many game makers who want a nice title screen are forced to either give up valuable sprite space for a custom-drawn logo, or use a tiny one and zoom in so it but looks very pixelated. Occasionally, someone will use a more advanced method to store images, such as Zep's PX9 or dw817's compression programs. These work well, but the minimum cost of 1000+ characters and 280+ tokens for decompression plus prite sheet rewriting, and/or screen-filling compressed strings can be overkill for many projects, so I came up with my own alternatives.</p> <p>Version 1.0 encodes logos as 1bpp images into 7 bit-per-character strings, then renders graphical features such as shadows, color gradients, and outlines in real time, without using the sprite sheet. A logo drawing function built with it takes between 85-130 tokens and achieves high levels of compression, though it's limited only to logos, and large ones with outlines can use ~50% of cpu cycles at 30fps.</p> <p>Version 2.0 uses a version of Run-Length-Encoding (RLE) to compress full-color graphics into strings. It's based on <a href="https://www.lexaloffle.com/bbs/?tid=38884">this system here</a>, originally devised by <a href="https://www.lexaloffle.com/bbs/?uid=45707"> @spellcaster</a>, but I was able to shrink the core functionality down to a fraction of the size and token count while adding a few new features. Like version 1.0 it doesn't use the sprite sheet at all and can handle images of any pixel dimensions. It offers a slightly less compact data footprint, but is far simpler to use, uses less CPU, and can handle any kind of graphics you want. There are 3 versions:</p> <ol> <li>Core version --the one demonstrated in the cart (74 tokens)</li> <li>Version with pre-conversion of strings to tables (104 tokens, lowers CPU usage about 40% further on average --could be useful for 60fps games)</li> <li>Version with pre-conversion and real-time vertical and horizontal flipping (161 tokens)</li> </ol> <p>Both 1.0 and 2.0 include instructions to encode your own logos or images, but version 2.0 is much simpler to use, just import or paste an image into the top left corner of the spritesheet, specify its size in pixels, run the cartridge, and follow the icons to encode the image and output a compressed string.</p> https://www.lexaloffle.com/bbs/?tid=38829 https://www.lexaloffle.com/bbs/?tid=38829 Wed, 15 Jul 2020 02:27:51 UTC Where do I get info on the unpack() function? <p>Lately I've heard bits here and there about a new function called <em>unpack</em> that seems to have some significant benefits, but after searching the BBS, the Pico 8 Wiki, and Google, I've barely been able to come up with any solid infomation on it or how to use it.</p> <p>If someone could point me in the right direction, I'd much appreciate it.</p> https://www.lexaloffle.com/bbs/?tid=38759 https://www.lexaloffle.com/bbs/?tid=38759 Sat, 11 Jul 2020 00:50:41 UTC Space Invaders Arcade <p> <table><tr><td> <a href="/bbs/?pid=78157#p"> <img src="/bbs/thumbs/pico8_sia01-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=78157#p"> sia01</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=78157#p"> [Click to Play]</a> </td></tr></table> <br /> Yes, this is yet another version of Space Invaders, but with this one I'm going for a direct port of the original arcade game. It's mostly complete, with functional gameplay and a working high score save system, but it needs some gameplay tweaks and a few graphical elements, and the sound effects are very much unfinished (I'm working on a version of the 4-tone march using subtones, but haven't ironed out the glitches yet). Anyway, in addition to going for authenticity, I also tried to code this very efficiently, and the whole game (including all graphics and sound), takes under 3KB and only about 1100 tokens.</p> <p>Any constructive criticism and general feedback is welcome, thanks.</p> <p>Controls:<br /> L/R------Move cannon<br /> X--------Fire</p> https://www.lexaloffle.com/bbs/?tid=38446 https://www.lexaloffle.com/bbs/?tid=38446 Tue, 16 Jun 2020 18:22:33 UTC Cloud Bounce <p> <table><tr><td> <a href="/bbs/?pid=77005#p"> <img src="/bbs/thumbs/pico8_cbex-5.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=77005#p"> cbex</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=77005#p"> [Click to Play]</a> </td></tr></table> </p> <p><strong>Version 1.1--</strong> Managed to shrink code to under 560 characters, making this a tweettweetcart<br /> <strong>Version 1.2--</strong> Fixed game over glitch, improved HUD<br /> <strong>Version 1.3--</strong> Removed lives (falling just sends you back to last checkpoint), added control tutorials and background decorations, tweaked controls</p> <hr /> <p>Join Squirt as he jumps and bounces in the sky through varying times of day, on his way to Cloud Land.</p> <p>Controls:</p> <p>Left/Right--------------Move<br /> Hold Z------------------Small bounce<br /> Hold X------------------Regular bounce<br /> Hold Z+X---------------Super bounce</p> <p>This is the full version of my single-tweet platformer. It's essentially the same game, but has a lot of important features added, including:</p> <ul> <li>Animated title screen</li> <li>Checkpoints</li> <li>Control tutorials</li> <li>An improved ending (the original was by necessity super abrupt)</li> <li>Birds!</li> </ul> <p>There's no sound or music, and no enemies, but overall I'm pretty happy with how much of a full game I managed to fit in a little over half a Kilobyte. Hope you enjoy it!</p> https://www.lexaloffle.com/bbs/?tid=38068 https://www.lexaloffle.com/bbs/?tid=38068 Fri, 22 May 2020 09:24:42 UTC Tweetris -- fully playable tetris in 560 characters <p> <table><tr><td> <a href="/bbs/?pid=76116#p"> <img src="/bbs/thumbs/pico8_tweetris1_1-3.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=76116#p"> tweetris1_1</a><br><br> by <a href="/bbs/?uid=42638"> JadeLombax</a> <br><br><br> <a href="/bbs/?pid=76116#p"> [Click to Play]</a> </td></tr></table> </p> <p><strong>Version 1.1: Fixed game over glitch that could occur when pieces were rotated the moment they appeared, pieces can now be rotated clockwise and counterclockwise.</strong></p> <p>This is a fully playable version of Tetris in fewer characters than a moderate-length email, including:</p> <ul> <li>Line removal</li> <li>Level progression (speed increases after every 10 lines cleared)</li> <li>'Lines cleared' display</li> <li>CW and CCW piece rotation</li> <li>Soft drop button (with slight delay when next piece appears)</li> </ul> <p>Controls:<br /> L/R-------move piece<br /> Down------soft drop<br /> X-------rotate piece clockwise<br /> O-------rotate piece counterclockwise</p> <p>Fitting everything in this small a space was pretty tough. I had to cut a few corners, like leaving out the next piece preview, as well as logic to allow movement of blocks once they land, so overhangs are a nuisance. With all that said, though, I worked hard trying to make this an actually decent version of tetris and not just a technical curiousity, so hopefully somebody actually enjoys playing it instead of just staring at the indecipherable souce code. Shoutout to 2dArray, whose Tweetjam Tetris and its genius piece indexing system I built on for this project.</p> https://www.lexaloffle.com/bbs/?tid=37812 https://www.lexaloffle.com/bbs/?tid=37812 Fri, 08 May 2020 08:53:36 UTC