User Input
Now that I have Ferb, my sprite, moving around I want to be able to control him with keys/buttons. That's what I'm tackling today.
Right now all Ferb does is run, waving his arms, from left to right. I guess he's excited he can at least do that. Let's get Ferb jumping for joy.
Right now all Ferb does is run, waving his arms, from left to right. I guess he's excited he can at least do that. Let's get Ferb jumping for joy.
PICO-8 allows you to control the program using either keyboard buttons or SDL-compatible controllers. Up to eight players have up to six buttons of input. For such a tiny "console", that's a ton of input and players. I'm going to keep things simple and just have one person control Ferb. What I learn today can be expanded to other players, if needed.
The syntax for testing a button/key (I'll just call it input) is:
btn( [i], [p] )
Where i is the input, and p is the player number. Since I'm only going to use one player, p will be 1. The parameter i can have six values, 0 through 5 which can be mapped to keys. I'll use the arrow keys: up, right, down, left which by default are 2,1,3, and 0.
One cool thing about PICO-8 is that it also maps these directions to language tokens. The tokens can be used in the i parameter in the btn call. Here is a screenshot of the tokens (2nd column) and how to get them.
So, to get the right button you type SHIFT-R in the PICO-8 editor. I'll be using the numeric mappings since I'll be using an external editor.
In order to get Ferb to jump I need to test for the up input and move him vertically. After a delay I need to move him back to where he was when he started to hop. I'll do this in the _update function.
First, I need a function to see if the up input was pressed.
Line 77 tests for the up input. Line 78 is used to offset Ferb's y coordinate. The variable hopDirection is used to track the actual hop. If it's 0, Ferb isn't hopping. If it is -1 he is moving toward the top of the screen (0,0 is the upper left corner). If it is 1, he is dropping back to the ground. I'm using hopDirection as a state flag. Speaking of the ground I need to know where Ferb's normal y coordinate is. I can deal with this in the _init() function. This is also where I initialize hopDirection to 0.
I also initialize the ground and set the initial y coordinate to the ground on lines 9 and 10.
Now, lets talk about line 14, hopDuration. When Ferb jumps, he has to go back to the ground, otherwise he'd just fly off. So a hop has an up and a down component, hence the need for hopDirection. I'll use hopDuration to track when Ferb starts dropping back to the ground. For the first half of hopDuration he'll move up, for the second half he'll drop down to the ground. I'm not being fancy and not simulating gravity. This hop is just a sawtooth pattern, but it's good enough for now.
Let's look at my actual hop() function.
Lines 46-48 are just a guard. If Ferb isn't hopping, just return. Line 50 changes Ferb's y coordinate based on the direction he's supposed to move. -1 is up, 1 is down. Lines 53-55 is where I toggle the direction. The term (ground - y) normalizes the y coordinate to fall into the hopDuration range. If that normalized value is greater than half the hop that means Ferb has reached his highest point and has to start going down. Line 54 sends him toward the ground. Lines 57-60 make sure that Ferb doesn't go underground. This can happen for values of hopDirection that are not 1.
I put it all together in _update().
Line 102 tests for the up input being pressed or if Ferb is already in the mid-hop. If he is, he hops. This is what it looks like when you run it. Click the image.
Pretty cool for a brute-force approach. But I discovered a problem. This is what happens when I hold the hop button down.
This happens because upPressed() can be tested while Ferb is "in flight". The simple fix is to put a guard in the code.
Lines 78-80 checks to see if Ferb is in the middle of a hop. If he is, we ignore the button. I should refactor that into its own function. Here is the updated animation.
That's much better. But he's too quiet. Next time, I want to learn about the sound effects editor and get Ferb making noise.
Here is the full my full cart with source code.
The syntax for testing a button/key (I'll just call it input) is:
btn( [i], [p] )
Where i is the input, and p is the player number. Since I'm only going to use one player, p will be 1. The parameter i can have six values, 0 through 5 which can be mapped to keys. I'll use the arrow keys: up, right, down, left which by default are 2,1,3, and 0.
One cool thing about PICO-8 is that it also maps these directions to language tokens. The tokens can be used in the i parameter in the btn call. Here is a screenshot of the tokens (2nd column) and how to get them.
So, to get the right button you type SHIFT-R in the PICO-8 editor. I'll be using the numeric mappings since I'll be using an external editor.
In order to get Ferb to jump I need to test for the up input and move him vertically. After a delay I need to move him back to where he was when he started to hop. I'll do this in the _update function.
First, I need a function to see if the up input was pressed.
Line 77 tests for the up input. Line 78 is used to offset Ferb's y coordinate. The variable hopDirection is used to track the actual hop. If it's 0, Ferb isn't hopping. If it is -1 he is moving toward the top of the screen (0,0 is the upper left corner). If it is 1, he is dropping back to the ground. I'm using hopDirection as a state flag. Speaking of the ground I need to know where Ferb's normal y coordinate is. I can deal with this in the _init() function. This is also where I initialize hopDirection to 0.
I also initialize the ground and set the initial y coordinate to the ground on lines 9 and 10.
Now, lets talk about line 14, hopDuration. When Ferb jumps, he has to go back to the ground, otherwise he'd just fly off. So a hop has an up and a down component, hence the need for hopDirection. I'll use hopDuration to track when Ferb starts dropping back to the ground. For the first half of hopDuration he'll move up, for the second half he'll drop down to the ground. I'm not being fancy and not simulating gravity. This hop is just a sawtooth pattern, but it's good enough for now.
Let's look at my actual hop() function.
Lines 46-48 are just a guard. If Ferb isn't hopping, just return. Line 50 changes Ferb's y coordinate based on the direction he's supposed to move. -1 is up, 1 is down. Lines 53-55 is where I toggle the direction. The term (ground - y) normalizes the y coordinate to fall into the hopDuration range. If that normalized value is greater than half the hop that means Ferb has reached his highest point and has to start going down. Line 54 sends him toward the ground. Lines 57-60 make sure that Ferb doesn't go underground. This can happen for values of hopDirection that are not 1.
I put it all together in _update().
Line 102 tests for the up input being pressed or if Ferb is already in the mid-hop. If he is, he hops. This is what it looks like when you run it. Click the image.
Pretty cool for a brute-force approach. But I discovered a problem. This is what happens when I hold the hop button down.
This happens because upPressed() can be tested while Ferb is "in flight". The simple fix is to put a guard in the code.
Lines 78-80 checks to see if Ferb is in the middle of a hop. If he is, we ignore the button. I should refactor that into its own function. Here is the updated animation.
That's much better. But he's too quiet. Next time, I want to learn about the sound effects editor and get Ferb making noise.
Here is the full my full cart with source code.