Log In  


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.

1


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


1

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

with formatting

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]