I'm a novice programmer, and am fumbling my way through making a game in PICO-8. One of the things that I want to do in my movement code is only have 4 directional movement, not 8.
My movement code is:
function move_player() --walkleft if (btnp(0)) then px-=8 end --walkright if (btnp(1)) then px+=8 end --walkup if (btnp(2)) then py-=8 end --walkdown if (btnp(3)) then py+=8 end end |
I use btnp because I want tile locked movement in my game. This might be achievable with btn, but I couldn't figure it out. One big problem with this is the lag from the initial press to when it moves again from holding the button down. Also, if you hold both up/right, up/left, down/right, or down/left on the arrow keys the player will move diagonally. This makes sense, of course, but it's not what I want.
My problem is that I do not know how to achieve what I want. My first thought is something along the lines of only accepting the most recent button input, using elseif achieves something close, but directions take priority in the order they're written, not pressed. I'd appreciate any help.
There are a few ways to do this, but they will depend on what you want the rest of the game to do. If you just want to make sure that diagonals do nothing and you have to press exactly one direction, something like this is simple and will work:
local dirs_pressed=0 for i=0,3 do if btnp(i) then dirs_pressed+=1 end end if dirs_pressed==1 then -- whatever goes here will only happen when one and only one direction is pressed end |
Forum formatting sucks a bit, but hope the gist got across.
I really appreciate the help, but that makes it so the player stops in his tracks when two buttons are held. It does stop diagonal movement so I can't really complain.
I could probably add onto that function and say something like if dirs_pressed==2 (insert something that allows most recent button to take priority), but idk if there's anything innately in PICO-8 to track the most recent button press.
Here's my idea:
The direction the player is going in is stored in a global variable. -1 means the player is standing still. At the start of your _update() function you check if the player is still pressing the direction button they were going in.
if not btn(direction) then direction=-1 end |
After that we'll do the movement.
for i=0,3 do if btnp(i) and (direction==i or direction==-1) then if i==0 then px-=8 elseif i==1 then px+=8 elseif i==2 then py-=8 else py+=8 end direction=i end end |
It checks for all directions if the button is pressed and if it's the direction the player is going in, or if they are standing still. At the end it sets direction to the direction we're going in.
You could also shorten the inner part to this and save a couple tokens.
if i<=1 then px+=(i-0.5)*16 else py+=(i-2.5)*16 end |
With formatting: pastebin
@blizzz The button held down first takes priority in this one. If you hold up, then hold left afterward, it'll keep going up.
It might be a problem with btnp though. It pauses for a second like it recognizes the second held button, but it decides to keep moving in the direction of the first.
Changing your first "btn(direction)" to "btnp(direction)" also ends up with the same problem elseif has where written order takes priority.
the pause you see is btnp() synchronizing across buttons for some reason:
https://www.lexaloffle.com/bbs/?tid=3671
Oh, I misunderstood what you want to do. Hm, to take the most recent direction you'll have to remember the keys that were pressed before. Or else it will flip between directions when two buttons are pressed.
I'm probably overthinking this, but this will work (I think?):
--globals px,py=64,64 --check if button was pressed previously function btn_prev(i) return 2^i==band(2^i,btn_) end function _update() --check if player is still moving in the same direction as last frame if not btn(direction) then direction=-1 end --check if there is a new button pressed for i=0,3 do if btn(i) and (not btn_prev(i) or direction==-1) then direction=i end end --move player if btnp(direction) then if direction<=1 then px+=(direction-0.5)*16 else py+=(direction-2.5)*16 end end --store current buttons btn_ = btn() end function _draw() cls() print("direction "..direction) print("x "..px..", y "..py) pset(px,py) end |
Edit: removed a mistake
I think I understand one part of the problem after deleting all of my posts and actually reading the question. Use btn (instead of btnp) and kick off a timer as soon as you get a btn event. Wrap all your btn checks in the timer so you only check them every X number of frames/updates.
The way I understand it, zach wants a movement system like in old school RPGs à la Final Fantasy or Pokémon.
@blizzz Thank you so much! It works great! I'm just wondering what the mistake was. The only difference I see is "return (btn(i) and 2^i==band(2^i,btn))" vs "return 2^i==band(2^i,btn)". I'm assuming the text in your post is the updated version and the link isn't?
Thanks again so much though! Yep, old school grid/tile movement is my goal. :)
@radcore Yeah after spending quite a few hours on my own today, I figured I'd need to switch to btn eventually, but it's probably going going to be just as tough as this was for me.
Yes, that's the mistake. It's not supposed to check if the button is currently pressed. The link is still the old version. It also works, but it doesn't do what the function name suggests. I copied it from a similar function in my game and didn't change it correctly.
Glad that it works for you. :)
Hey, just wanted to update and say that I added a frame counter/timer to btn like radcore suggested and it works functionally the same as btnp without the initial hitching. All I had to do was make blizzz's function check against the frame count.
Thanks for all your help! :)
[Please log in to post a comment]