TO LOAD THIS CART in immediate mode, type
load #playback_a
VVhat's new ?
(01-17-22)
✅ New BTN() can now be called with no arguments, returns numeric of all buttons, thanks to @SmellyFishstiks.
✅ Beefed up the documentation and usage instructions.
This program will let you record and playback keystrokes, including saving your code with those compressed keystrokes in very small code indeed.
First off, the record and playback routine is 436-characters total, so this entire method is pretty small at that. Add about 46-characters for each of the functions record()
and play()
and you're home free !
Further I did not make this game. It is JELPI, written by @zep, a demo game which is included when you purchase Pico-8.
What I have written are two primary functions. One to record keystrokes as you play the game and transfer them to the clipboard as new source code - and the other is to simply play them back as you have listed.
This replaces the btn()
function with my own. Include the 3-functions that + typing at the top of your code: _b0=btn
and you are perfectly done. Nothing whatsoever else is needed. See the code below on usage:
_b0=btn function record() _bm=1_bp=1_bt=0_bd="" end function playback() _bm=2_bp=1_bt=0 end function btn(p)local b=_b0()if _bm==1then if b!=_bl then if _bd>""then _bd=_bd.._bt..","else _bd=""_bt=0_bp=1end _bl=b _bd=_bd..b..","_bt=0else _bt+=1end if _b0()==15then printh("btnpb={"..sub(_bd,1,#_bd-1).."}\n","@clip")stop()end return _b0(p)elseif _bm==2then if _bt>0then _bt-=1else _bs=btnpb[_bp]_bt=btnpb[_bp+1]_bp+=2if _bt==nil then _bm=0end end if p==nil then return _bs elseif band(_bs,2^p)>0then return true else return false end else return _b0(p)end end . . . (( The beginning of your code, _init() or what have you )) |
When you choose RECORD, you can play the game as normal with no changes to your sourcecode. Every keystroke is recorded through a new version of BTN() as you can see. To stop recording at any time by press all 4-arrow keys at the same time, this will also stop the cart and return back to the editor. Then you can choose a line of your source code and press CTRL+V to paste the new button set you just used to play your game. It might appear like this:
btnpb={0,84,2,109,18,31,2,29,0,74,2,49,0,49,2,29,0,14,1,84,0,19,2,14,0,44,2,9,18,3,22,19,6,9,38,24,6,14,2,24,0,39,1,169,17,23,0,79,2,184,0,34,2,9,18,31,2,89,0,79,16,15,0,84,1,69,0,4,16,11,18,3,2,14,0,49,2,164,18,51,2,109,18,19,2,59,0,104,1,29,0,54,2,4,0,139,2,9,22,23,38,39,6,4,2,9,0,59,2,139,34,159,0,4,16,3,18,31,0,49,1,9,17,71,1,24,0,24,16,23,0,34,1,34,0,49,1,29,0,49,1,14,0,79,1,19,0,79,16,19,17,3,1,9,0,39,1,54,0,34,2,114,18,15,2,154,18,27,2,104,18,39,2,4,0,14,1,39,0,24,2,14,0,79,2,14,0,39,16,15,0,89,2,54,18,27,2,14,0,44,2,209,18,39,2,79,0,9,1,74,0,29,2,9,0,44,16,15,0,59,2,94,18,27,2,74,0,49,16,23,0,59,2,124,18,51,2,49,18,15,2,39,0,24,1,24,17,27,1,9,0,59,2,9,18,27,2,9,0,19,1,9,0,69,15} |
Then select PLAYBACK after the btnpb=
definition and run your cart again. You will see all of your keystrokes played back exactly as you did them with exact timing and everything. When the end of your keystrokes is reached, game input returns to normal and you can continue playing your game right where it was left off.
There is slight compression in here in that it counts the number of cycles you repeat a keystroke or series of keystrokes. So it doesn't just record 30-numbers for 30-frames of data, that would make the data too big. In fact to play this entire demo game of JELPI above from beginning to end took only 265-numbers or true 687-characters of space.
There are more details and instructions in the cart listed above, examine them for additional assistance.
And that's it ! Choose RECORD first and play the game as well as you can. When done recording press all 4-arrow keys together. Return to your code and Press CTRL+V to paste the new btnpb
(button playback) string. Then select the PLAYBACK function instead of record, run the code, and sit back and watch it.
If you want to, paste your own Pico-8 cart playing back your own movements for the game, to show everyone else how well you can play it.
If greater compression over raw numbers is desired, (6-bit characters for instance), I can certainly write that for you and increase the btn() function in size slightly with higher compression in the data table.
Thanks to @Cadaeib65 for getting my brain spinning on this.
Hope This Helps !
Brobgonal cause that's the point.
Also this is sick! I would love to make a TAS for my game and watch it back or something hahaha
Hi @brobgonal. That is the point. It is the first time to be able to take over the controls of Pico-8 and play them back as someone can record them, in this case what you see above is me playing the level.
If you own Pico-8, you can play this game from your DEMO folder. Failing that @zep did post the original JELPI found HERE:
Hi @SmellyFishstiks. Thanks ! I was beginning to wonder if what I wrote would not be understood.
And yes you can take those few functions, put them in your code, record a speedrun, press the 4-arrow keys to exit. Choose a line of your code to paste the playback string.
Select playback()
post the new cart in your same or different thread, and you are all set. Let me know too ! So far I've only seen my playback code for JELPI.
Truly @zep should already have this feature of record and playback built-in. This, rewind, frame forward, frame backward, cheat search, save state, and load state.
Ya I'll have to see if it works on other carts, I'm not sure that a feature builtin to pico like this would be what I want but ya it's fun to play with.
Yes, please let me know, @SmellyFishstiks. I want this to work properly all the way around. I know there are some Pico-8 games people really do want to speedrun, and hopefully this tool will help in that endeavor.
Thanks, @SandwichBlam. I'm hoping someone posts a CELESTE or modified CELESTE speedrun cart with it. I think that would be interesting to see - and would verify my program is working 100% correctly.
This is like handing your little brother the controller because he wants to play but it isn't plugged in so he just watches you play thinking he's playing
Ah, pretty well, Scrub. I was thinking of flashing a green or red light or something in the corner showing recording or playback, but that might interfere with gameplay.
If it's truly desired, I can add it.
I wrote this so it would help others post a speedrun of their favorite games. Or maybe even nail down a particularly tricky bug that only appears one way.
You record up to that point where it crashes, post your recorded keystrokes. Then give that text or cart to someone where it dies and they can easily track and trace the problem by watching the playback.
As mentioned, adding this ability inside Pico-8 I think would be beneficial and increase interest in using it over other more limited free Fantasy Consoles.
hmmm I tried it with my game to start like this:
-- [[ game playback recorder --written by dw817 (01-17-22) --standard ⌂ pico-8 license function record() _bm=1_bp=1_bt=0_bd="" end function playback() _bm=2_bp=1_bt=0 end _b0=btn btnpb={0,29,2,95,3,4,1,74,3,9,2,113,0,453,4112,23,2,89,0,17,1,44,0,41,6,5,7,14,15} --record() -- call btn() as normal function btn(p)local b=_b0()if _bm==1then if b!=_bl then if _bd>""then _bd=_bd.._bt..","else _bd=""_bt=0_bp=1end _bl=b _bd=_bd..b..","_bt=0else _bt+=1end if _b0()==15then printh("btnpb={"..sub(_bd,1,#_bd-1).."}\n","@clip")stop()end return _b0(p)elseif _bm==2then if _bt>0then _bt-=1else _bs=btnpb[_bp]_bt=btnpb[_bp+1]_bp+=2if _bt==nil then _bm=0end end if band(_bs,2^p)>0then return true else return false end else return _b0(p)end end --]] playback() |
And at first I wasn't realizing why it wouldn't end recording when I pressed down ⬅️➡️⬆️⬇️,
Then I realized that you have to have a btn() call I guess which doesn't get ran when the player dies.
But after I figured that out I put my btnpb in and it keeps on giving me this error:
runtime error line 31 tab 0 function btn(p)local b=_b0()if _bm==1then if b!=_bl then if _bd>""then _bd=_bd.._bt..","else _bd=""_bt=0_bp=1end _bl=b _bd=_bd..b..","_bt=0else _bt+=1end if _b0()==15then printh("btnpb={"..sub(_bd,1,#_bd-1).."}\n","@clip")stop()end return _b0(p)elseif _bm==2then if _bt>0then _bt-=1else _bs=btnpb[_bp]_bt=btnpb[_bp+1]_bp+=2if _bt==nil then _bm=0end end if band(_bs,2^p)>0then return true else return false end else return _b0(p)end end attempt to perform arithmetic on local 'p' (a nil value) at line 0 (tab 0) |
where p is nil.. so ya It's really cool but man I just don't know how to use it.
(this was in the bottom of init. though I tried elsewhere and got the same issue)
ya when printing out p the first one is always nil..
OH wait, this is cause I have a btn call somewhere that's just nil or something isn't it
if btn()~=3 and (btn(0) or btn(1)) or a.drilling then a.ifmove = true a.mt += 1 end |
ya I have this. is there anyway you could make a failsafe if a btn() call doesn't have any arguments and is just for getting the bit field?
function btn(p) if p then local b=_b0()if _bm==1then if b!=_bl then if _bd>""then _bd=_bd.._bt..","else _bd=""_bt=0_bp=1end _bl=b _bd=_bd..b..","_bt=0else _bt+=1end if _b0()==15then printh("btnpb={"..sub(_bd,1,#_bd-1).."}\n","@clip")stop()end return _b0(p)elseif _bm==2then if _bt>0then _bt-=1else _bs=btnpb[_bp]_bt=btnpb[_bp+1]_bp+=2if _bt==nil then _bm=0end end if band(_bs,2^p)>0then return true else return false end else return _b0(p)end end end |
maybe not the best way but something like that?
Hi @SmellyFishstiks:
Can you post the entirety of the code, please ? Full cart. I'll see what I can do to get it working and recording/playing back correctly. And yes I likely need to write a function for BTNP() as well.
Currently my record/playback engine only works with BTN(), not BTNP() nor any mouse functions.
ya I was editing my msgs to not spam, it was a lose btn() check but like ya could there be a failsafe?
When I added that failsafe it works now
I think I understand, @SmellyFishstiks. You want to be able to call btn() with no arguments and it returns the correct value instead of TRUE or FALSE.
Let me examine my code.
ya I want to use btn() by it's self to get just the state of the buttons, I use this as a cheap way to make sure you aren't pressing left and right at the same time.
And I'm sure I'm not the only one that has used this either
OK, rather than change all the code, @SmellyFishstiks, replace this single line into yours to see if this fixes it:
function btn(p)local b=_b0()if _bm==1then if b!=_bl then if _bd>""then _bd=_bd.._bt..","else _bd=""_bt=0_bp=1end _bl=b _bd=_bd..b..","_bt=0else _bt+=1end if _b0()==15then printh("btnpb={"..sub(_bd,1,#_bd-1).."}\n","@clip")stop()end return _b0(p)elseif _bm==2then if _bt>0then _bt-=1else _bs=btnpb[_bp]_bt=btnpb[_bp+1]_bp+=2if _bt==nil then _bm=0end end if p==nil then return _bs elseif band(_bs,2^p)>0then return true else return false end else return _b0(p)end end |
If it does, I'll update the master.
Master cart updated.
It seems to work, Though after watching this I see as well that the rng state could mess things up (ex the clouds.)... would have to save the seed you started recording on and srand at the start..
But for replaying stuff this works great, Will love to use it if I have enough tokens left to record runs and stuff.
Owow I love your Rescue Bird, @SmellyFishstiks. That is neat. I am totally in the field I want to be. Helping out game programmers with useful functions and utilities. :)
As for tokens, hmm ... I could write a function that waits until you press CTRL+V and then PASTES the playback script from the clipboard directly into the array - skipping adding it to the source-code, if you think that will help.
. . .
Let me update the main engine with this new function.
ya new version works. once I actually have stuff to do in my game it'll get interesting.. But ya cool tool thanks for making it
You are very welcome, @SmellyFishstiks. I hope you other guys and gals find it useful too. I'm - already working on something else. :)
Write if you have any questions or comments about this Playback project.
Oh my god thank you so much @dw817 ! I will ask you again later when I have PICO-8. Do you have a discord or smth that we could use to not flood this place ? By the way I don't think the game I want to TAS has any RNG.
I'm glad you like it, @Cadaeib65. :) Something overdue for Pico-8 I guess ...
I was on discord at one point. Understand I'm never a leader. I always try to follow - if the path is not too difficult. If you yourself open a Discord I will accept your invite - but I won't open one personally myself.
Or you can reach me on my Writer's page by creating an account there for messaging. Click dw817 on this page to see the link. Click that link, let the page come up, then click at the top right, "Sign Up!" Once logged in you can send and receive private mail. I read, write, and reply to messages on that site daily as it's my home on the internet.
That's the best I can do.
There is is pico-tas discord I discovered recently @dw817 https://discord.gg/JWvDMda2
I like your colour taste. The game is not only properly programmed(I guess) but also looks fantastic.
Hi, @WallacePrio. Thank you however this game is not mine. It is from @zep, our lord and master of Pico-8.
What I wrote here is a way to record your movements and play them back in any Pico-8 cart, especially for use in Speedrunning. That is all.
I can't get the code to work, can you help me understand what to do?
edit: nvm, my keyboard won't register all four keys at once so I changed it to the enter key
edit2: still not working :(
Does anyone know why it is not properly recording my buttonpresses?
Hi @Sup3rAw3som3Gam3r:
First off be certain that the statement, b0=btn
is NOT included in any function, not even INIT. Have it as the very first line of your code if you like.
Let me know if you're having problems after that.
just to make sure I didn't do something stupid, can I paste this into a cart to add recording to it?
edit: oh wait, I maybe got it working!!!
Can you explain the numbers? I want to try to do tas with this
(just for fun)
@Sup3rAw3som3Gam3r:
Did you get it working to your satisfaction ?
The numbers are merely the button presses combinations and the durations they are pressed. It's pretty straight-forward.
I don't understand how the delays and stuff (for example, there is no btnp(16) in pico-8)
superawesomegamer pico8's button input is a byte that holds a bit field of the button presses per player
(you can test this by doing print(btn()) )
You're very welcome. Here now, please show me a playback of some game you have in mind for this. I'd like to see that. Thanks !
setting the stop key to tab is helpful for me
(also, it seems the 2nd num is the dur)
That could be, @Sup3rAw3som3Gam3r. I just kind of fired and forgot the code. Playback code is good for action games, not turn-based so it's not something I am going to use a lot of.
I can examine the code, but if you are already making mods then I think you see how it works.
And good for that ! Still though, let me know when you get a video or P8 all done for playback for one of your games. It would be interesting to see my code in use for something you wrote.
Wow ! Pretty good use of the playback. It's a shame you can't actually edit P8 videos in Pico-8 - or to control frames forward and backwards during actual execution.
I was going to develop something like this yet it would be pretty limited. Still, a programming language that can run both forwards and backwards would be a treat indeed.
[Please log in to post a comment]