Hey All! PICO-8 0.2.2 is now up on lexaloffle, Humble, itch.io, and for PocketCHIP.
This release follows a pattern set by previous 0.2.* updates in that I set out to fix a bunch of bugs and resolve design details, but in doing so, went down some deep rabbit holes and came out the other end with brand new features. As a result, some of the new features are on the advanced side, and this post will be likewise be more technical than usual. But I hope everyone can find something fun to mess around with!
SFX Filters
At the bottom of the SFX editor, you can now find 5 switches that alter the characteristics of each instrument. You can get a much wider variety of sounds and textures now, but they're meant to feel like variations on the existing instruments rather than completely new ones. I settled on this scheme after working on Voxatron's sound system and found that I could boil the set of parameters I wanted down to just 7 bits of information -- which is fortunate because there were only 7 unused bits left in the SFX data!
The 5 switches are:
- NOIZ: Generate pure white noise (applies only to instrument 6)
- BUZZ: Various alterations to the waveform to make it sound more buzzy
- DETUNE-1: Detunes a second voice to create a flange-like effect
- DETUNE-2: Various second voice tunings, mostly up or down an octave
- REVERB: Apply an echo with a delay of 2 or 4 ticks
- DAMPEN: Low pass filter at 2 different levels
SFX instruments (the green ones) can also use these filters, so it is possible for example to have a detuned square wave in the same SFX as a dampened triangle. When both the parent SFX and the SFX instrument have the same switch set at 2 different levels, the greater of the two is used.
Here's @Gruber explaining filters, along with a newly added control over the length of each SFX (useful for implementing uneven time signatures):
And here's @tesselode putting them to good use:
[tweet ]
The BBS SFX player (copy a range of patterns and paste them as text) works with the new filters, but is still janky on mobile:
P8SCII
In previous versions of PICO-8 there were some characters in the range (0..15) that were sitting around doing nothing useful when you printed them. That's clearly no good, so 0.2.2 has introduced a new control codes that allow control over things like text formatting, cursor position and even sound generation. Along with some kana improvements, 0.2.2 now has complete set of 256 characters -- or P8SCII (PICO-8 Standard Code for Information Interchange).
An example: the "command" character (6) can be written as "\^" followed by a command character and sometimes a parameter:
cls()cursor(20,20) ?"\^iinvert\n" ?"\^ppinball" ?"\^wwide \^ttall\n" ?"\feset colour\n" ?"\#5solid background\n" |
Character 7 ("\a") can be used to play audio using a compact description
?"\a" -- beep ?"\asfc3ccbdcbae2ee" -- play a wee ditty |
This might be useful for squeezing extra sound effects into a cart (or tweetcart), or for simple sound effects in type-in zine listings.
For more details on the P8SCII control characters, see the manual: https://www.lexaloffle.com/pico-8.php?page=manual
Custom Fonts
A custom font can be enabled using control character 14 (or by setting bits 0x81 at address 0x5f58). A fixed character width and height can be specified at 0x5600,0x5602, followed by 8 bytes per character (8x8 bitmap) starting from 0x5608 (character 1). It can be a little fiddly setting the correct data up, so to get started, here's a wee tool that grabs font characters from the spritesheet and generates a snippet:
LOAD #FONT_SNIPPET |
The output snippet is 7 tokens, and can be pasted into your cartridge. Here are some example fonts and their snippets:
The one on the right is from @thattomhall's SPRITEFONT cartridge: https://www.lexaloffle.com/bbs/?pid=75073#p
Snippets: https://www.lexaloffle.com/dl/files/font_snippets.txt
(that reminds me, there needs to be a good way of posting long snippets like this on the bbs)
Sprite Fill Patterns
It is now possible to apply fill patterns to sprites, which includes spr(), sspr(), map() and tline(). A special bit (0b0.01) is set when calling fillp() to turn this feature on. Each sprite pixel is mapped to TWO colours using the (previously undocumented!) secondary palette, and these two colours are used to draw the pattern.
There are more technical details in the manual, but here's an example:
If you add the following fillp call to /demos/jelpi.p8 in init_level() (tab 5):
cls()reset() fillp(♥\1|0b.011) |
The result is:
There's a lot going on here!
- I've added the call to fillp() after reset(), as it sets fill pattern state to default values.
- ♥\1 is a fill pattern constant with \1 (integer divide) to remove the alpha bit (0.5)
- 0b.01 (0.25) is the bit that turns on fill patterns for sprites
- 0b.001 (0.125) is another bit that applies the secondary palette mapping to ALL drawing functions, including a rectangle that is drawn at the bottom of the mountains.
- the default secondary palette is being used, which consists of the original colour + a slightly darker counterpart. For example, 12 (0xc) maps to 0xdc. This could be changed with pal(12,0x78,2).
I think there there will be some interesting unexpected ways to use this feature, but a nice immediate example by @johanp is to add dithering to textured polygons drawn with tline:
[tweet ]
(as the truck turns around, you can see a few frames of checkerboard dithering without destroying the look of the texture)
MULTIPOKE
POKE, POKE2 and POKE4 can be given up to 2048 values to poke into memory in sequence, similar to the DATA statement found in early BASIC variants. Try this to write 6 pixels to video memory:
poke(0x7000,8,9,0xac) |
This can be used with unpack() and split() to dump a bunch of values to ram.:
poke(0x7000,unpack(split"8,9,0xac")) |
Values can be read out in a similar manner:
a,b,c=peek(0x7000,3) -- 8,9,0xac |
There is a limit of 2048 values in both cases, which means you can copy up to 8k in one go using PEEK4/POKE4.
Locked Mouse Pointer
This is a feature in 'devkit' mode which is normally intended for making development tools and exported binaries, but of course you are welcome to use it for whatever you like :)
(but keep in mind that some players using the BBS don't have a mouse or keyboard)
POKE(0x5F2D, flags) -- where flags are: 0x1 Enable 0x2 Mouse buttons trigger btn(4)..btn(6) 0x4 Pointer lock (use stat 38..39 to read movements) |
.. so you need to poke(0x5f2d,0x5) to enable mouse with lock. stat(38),stat(39) will then give the mouse movements in desktop pixels rather than PICO-8 ones, so that the window size is irrelevant; they can be thought of as abstract motion events with a comparitively high sensitivity. PICO-8 attempts to match mouse movement when entering and exiting locked mode, but this requires setting the mouse cursor at the operating system level, which is not supported in web. In any case, you might get more consistent results by setting mouse lock once at the start of your cartridge and leaving it there (other mouse events will still work as usual via stat 32,33,34).
You can see it in action in @freds72 and @paranoidcactus's POOM: https://freds72.itch.io/poom
Custom Menu Control
Menu item callbacks added with MENUITEM can now elect to keep the pause menu open by returning TRUE, and can also detect if the left and right buttons were pushed. The callback takes an integer parameter that is a bitfield of left and right button presses. Buttons 4..6 all map to each other (can't tell them apart).
For example:
function my_menu_item(b) if(b&1 > 0) menuitem(_,"left!") if(b&2 > 0) menuitem(_,"right!") if(b&32 > 0) menuitem(_,"selected!") return true -- stay open end menuitem(1, "select me", my_menu_item) function _draw() cls(5) print(t()) end |
Another example:
LOAD #CUSTOM_MENU |
Web Gamepad Improvements
The default PICO-8 0.2.2 HTML exporter (and the BBS web player) now includes @weeble's improvements that allow the DPAD mapping, and better hotplugging / controller indexing behaviour.
Thread: https://www.lexaloffle.com/bbs/?tid=41293
Optimisation / CPU Changes
PICO-8 0.2.2 underwent a fairly aggressive optimisation pass; heavier cartridges use around 20% or in extreme cases 30% less cpu / battery life. I did some further tweaks of CPU costs to keep the theoretical host cpu ceiling as low as possible, which helps a lot on devices like the Raspberry Pi 2 & 3. Let me know if you have a cart that's running too slow on 0.2.2 -- I don't think there are many affected, and I'd be happy to help optimise it by swapping in the new binary operators etc. See the changelog for other cpu cost changes.
The following are some notes for the curious -- this shouldn't affect performance considerations when making PICO-8 carts. There was/is a lot of potential to change cartridge behaviour in subtle ways however, so as always, please let me know if you see something weird that was working in previous versions, even if it seems like a small thing.
There were 5 areas that needed improvement:
Map Export
EXPORT FOO.MAP.PNG |
.. to get a full-scale png of your map!
Changelog
This is all super cool! I've been experimenting with the new SFX filters and new text formatting, and the results have been awesome! An excellent update all round :D
SWEET! Such a great update. Woo hoo! Glad the font was helpful.
Congrats on the release @zep! This is a lot!
The upside to being so slammed at work that my gamedev time is down to zero is that I get to sit back and watch all the pros do amazing things with these new features and pick up some tips to use when life slows down again :).
Wow, this is amazing. I'm constantly marveling at how even though there are all these new features in each update, you still end up making it feel like polish on an already amazing product. Almost like filling in gaps and holes, rather than new protrusions out of the original product. Like, "Oh, huh... yeah, that felt like it was missing before."
Anyway, congratulations on this update. I can't wait to play with all the new features! :)
I had literally just been wishing an update adding some of the hidden music effects (namely the filter) and was elated to find there's even more options than I had originally hoped for.
PICO-8 has been such a fun tool for creating chiptune music and this has already taken brought new life to several tracks that were feeling stale/same-y.
One of my issues was that many of the voices can be very harsh so the filters are a great addition. Having more than one is even better!
I guess this might be a good opportunity to ask if there is any possibility of adding a global speed value in the future that you can sync tracks to? This would make it much easier to adjust multiple tracks at once rather than having to go to each individual one and adjust speed on each track.
The only other feature I can think of would be
a transpose feature to easily adjust song keys but that might be too much for what this is trying to achieve. Either way, this is a fantastic update so thank you very much!
> The only other feature I can think of would be a transpose feature to easily adjust song keys but that might be too much for what this is trying to achieve. Either way, this is a fantastic update so thank you very much!
There is a transpose feature for individual lines - select the lines you want to change within the SFX and type Shift+a note to transpose the interval from middle C to that note - so, like ... if you had a UI where you could select a line of SFX and edit them all at once ... idk, I could see either or both.
Will you add multi-channel music export in 0.2.2b or some version afterwards? Nonetheless, congrats for these new filter switches and congrats on this update in general!
hmmmm, does the custom font feature allow us to change the font in the built in editor?
I know with all the amazing new features here this one might get overlooked, but the chained peek4 and poke4 are so fantastic! It finally marries the Lua RAM and cartridge RAM in a very intuitive way, which means you can now move data between them seamlessly.
Among other things, loading new image data into the sprite space from strings just became a whole lot easier. Cartridge ROM data can also be peeked out to Lua for long-term storage in the massive 2MB, without using memcpy and clobbering some other part of the 8KB RAM. So much potential here, you could even process a screen effect entirely in Lua. Pico-8 just got shaders. XD
@StinkerB06 Like, exporting all the individual channels as separate .wav files?
hi to all :)
and thanks to @zep for pico-8. It is great!
I hope for a mobile version (Android) :D
I have downloaded this version and i think i found a bug:
This new version give me a syntax error in this function.
With previous version it wrks and it works with previous version.
I have the same error also in the cart that i have uploaded before.
(https://www.lexaloffle.com/bbs/?tid=38736)
function scr_msnlist(dt) local mh=_mhl[ui_op] if ui_draw() then foreach(_mhl,ui_opt) ui_print('dump cantina - job ('..ui_op..'/'..#ui_opl..')\n') draw_fct(mh.fct) local st='\n'..mh.tit..'\nlevel:'..mh.lv..'\n\nwe need to ' if mh.trg then st..='steal a ' else st..='recover a ' end st..=mh.it.nme..' from ' --if mh.trg then st..=mh.trg.nme..'`s ' --else st..='our abandoned ' end st..=mh.lnm --if mh.lnm=='starship' then st..=' orbits around' --else st..=' located on' end st..=' a planet in this sector.\nyou will receive '..mh.cr..'cr on success.\nexact location will be provided upon acceptance of the job.\n\nremember to buy medikits!' write(st) ui_print('⬆️previous ⬇️next',0,116) ui_btnlbl('begin mission','leave cantina') end ui_read() if btnp(🅾️) then psave();rcast_init(126,62) ui_clear(scr_travel,{md=msn_level(mh)}) end end |
returns this sintax error
syntax error line 181 (tab 2) else st..='recover a' end ')' expected near 'end' attempt to call a string value |
With some test I found that the problem is this two lines:
if mh.trg then st..='steal a ' else st..='recover a ' end |
This syntax seems correct
Thanks @ViperSnake75 -- I found the problem and will include a fix in 0.2.2b soonish (waiting for a few more bugs to surface)
I think there might be another problem: I tried using the left and right button menu callbacks in my game, and for some reason it seems that whenever i leave the menu, the first left or right press also "leaks" to the game itself?
I edited @weeble's joystick test app to demonstrate: https://www.lexaloffle.com/bbs/?pid=nupeziwozo
ok got it for 0.2.2b, thanks @rnd
(all buttons are supposed to be ignored after exiting the pause menu even if they are held down while exiting)
In my case, I specifically released buttons before exiting the menu, but either way a fix for that bug would be awesome!
I almost finished another update for my PICO-8 port of Donsol, which adds toki pona to the list of languages (and also makes it easier for others to implement their own languages!), and it uses the left and right keys in the menu to select the language. Right now, the moment one sets a language and exits the menu, it moves left or right one spot, which on the title screen might lead to picking the wrong difficulty.
I suspect the NOIZ filter for Instrument 6 will be quite good for horror games. It sounds quite similar to the static noise player after the minigames in FNAF 1/2. I'm excited as heck!
@zep Found a slight text-selection glitch when using glyphs. When selecting text with the mouse from left to right, the character after a glyph won't get selected until you select two characters past the glyph. If you select from right to left, it works fine. Here's a GIF:
Another question: I noticed that in PICO-8 itself, the black color is a full black (#000000), but in exported images, it's #020408, to the point that the black color on the FAQ's palette is also #020408. Is this a bug or a feature?
@zep there seems to be a bug in a certain case of line-drawing where the pixels wrap around to the other side of the screen:
Hello @zep, I think I found a bug where my game doesn't scale properly in the web player. It seems to draw my rectangles and lines in my pong game possibly at the display's screen size instead of pico 8 scaling the 128x128 display to fit the window. I was able to copy my code back into version 0.2.1B on a different computer to provide the second screenshot. The third picture is how I discovered it on my phone's web browser :o
@zep Request to make the "|" control character move the cursor by P0-8 pixels instead of P0-16 pixels.
Rasoning: The "-" control character moves the cursor P0-16 pixels but the cursor will also automatically advance to the right on the next character. So with "-" you have authority to wiggle a character left or right.
With the "|" control character the cursor won't move additionally. So P0-16 will only gives authority to wiggle a character upwards. But I'd like to wiggle it downwards as well! If only to wiggle it back down after doing somthing like this:
Adding an "\n" doesn't help since that resets the x position. The above spooky font is done using "\v" now since "|" literally can't do it. Applies to "+" as well.
Please Zep, all I want is a spooky skeleton font as a treat!
@zep How do you terminate the "\a" character. Let's say I want to print out a line character by character like in many JRPGs
This is the code I used. I just add to add "\^1" characters in front of every character in a string.
function slowify(s) local ret="" for se in all(split(s,"")) do ret..="\^1"..se end return ret end |
But then if I also want each character to make a sound and add the "\a" character like this
ret..="\a\^1"..se |
I hear a single complex sfx and I see no text. And tweaking the delay seems to actually change the melody. So I think all of the string behind the "\a" character seems to be interpreted as melody data. If this is not just ab bug but intended, how is it possible to "escape" the "\a" character?
Ah it seems like a space does the trick!
so
ret..="\a \^1"..se |
@zep I found an error in the documentation.
A custom font can be defined at 0x5600, consisting of 8 bytes per character * 256 characters = 2048 bytes. Each character is an 8x8 bitfield (1 bit/pixel), where starting from the top, each row is a single byte starting with 0x1 on the left. The first five bytes (character 0 is never drawn) describes attributes of the font: 0x5f60 character width in pixels (can be more than 8, but only 8 pixels are drawn) 0x5f61 character width for character 128 and above 0x5f62 character height in pixels 0x5f63 draw offset x 0x5f64 draw offset y |
0x5f60 is the secondary screen palette. You probably meant 0x5600, 0x5601, 0x5602... etc here?
On a whim, I was playing some older games found using 'search' in SPLORE. A couple of games seem to have odd freezing behavior.
Pico Off Road freezes temporarily between tracks as it builds them. This is okay, I can hang with that, no biggie.
However, this problem can become softlock as it does in 'Picoh-Mummy'. Starting a game, everything locks, but music still plays. Controls do nothing, so it must be Alt+F4 (windows version) quit.
Picoh-Mummy is here for testing/confirming bug:
https://www.lexaloffle.com/bbs/?pid=56879#p
@zep I'm not sure this bug? has been reported, assuming it is one.
print("\asfc1") -- plays C1 print("\asfd1") -- plays D1 print("\asfe1") -- plays E1 print("\asff1") -- plays F2 ! print("\asfg1") -- plays G1 |
IOW, F1 plays as F2, i.e., same as calling print("\asff2").
I found myself wishing @zep's font tool would also support importing a font from a snippet... so I extended it! https://www.lexaloffle.com/bbs/?tid=42560
BUG: go into sprite editor. Hit H. Go into map editor and it will be in tile number view.
Also other weird behavior that isn't always replicatable:
Choose larger sprite size in sprite editor, should reflect i n map editor but sometimes doesn't.
About the new menuitem parameter:
I noticed that it really only returns 1 for Left press, 2 for Right press, and 16 + 32 + 64 = 112 for X, O or Pause press. Holding multiple buttons will only react to the last button pressed, and never sum bitmasks (except for X, O and Pause, but they are always summed together).
I thought the advantage of using a bitmask was to detect combo like Left+X? Is it intended?
It seems easier to just check equality with 1, 2 and 112 if bitmasks are not used. That would make me spare a few characters too...
Note: I'm trying to get as many details as possible while improving the fan wiki page (https://pico-8.fandom.com/wiki/Menuitem)
I think the behaviour is intended. We used to have only OX to activate the item, then we got left/right to select from options. Combos are not meant to be used there.
(I’m surprised to read about Pause; I expected it to close the menu, not activate the item callback)
[Please log in to post a comment]