@SweetShark: I hadn't really thought about it. I have a nebulous plan to send word of its completion to Lab Zero some way, and post it on two forums I'm active on. I also got an offer for promotion from a software dev with a okay amount of reach who I contacted for an unrelated reason. Getting the thing playable start to finish is the current goal. I'll think more about specifically press when I send it out to testers, probably.
Let's talk about input and input buffers. This will be a long post, much of which isn't specifically about Indivisible. It's actually... kind of a dumping ground of thoughts for things I talk about often.
In the few game communities I'm active in, I'm known for being a bit of a stickler about input. I do hold my own games to that standard, though. Even before I was considering adding an input buffer to Indivisible, I took pains to make sure you got what you pressed.
That's me running right with bare hands selected, then pressing left, down, attack and switch weapon in the same frame, then releasing all buttons the very next frame. I get a crouching, left facing, axe attack. And you might think, "Well, Kasumi, that's what's supposed to happen." But I've played games where I'd get a crouching, left facing, hands attack because the weapon switch was processed later on the frame. Or I'd get a standing, right facing attack because I wasn't yet facing right, and wasn't yet crouching when the attack button is pressed. Worse, I've played games where my press of attack doesn't trigger any kind of attack, because switching direction (or something else) ended logic for the object for that frame.
One of the particular things I get on people about is filtering left+right and up+down. If you have code that's like, "If button right is held, add acceleration to character" and "if button left is held, subtract acceleration from character" this means if I'm running at any speed and press the opposite direction, the net of those two things is zero and I can move at a steady, slow rate. An
else if fixes that particular problem but...
If you have the code for acceleration in one place, and the code for which direction to face in another you can have the left facing run animation play while you're moving right. Here's Super Mario Bros:
Now, it might seem like you can just fix animation logic. "I get a left facing animation while running right in my game. I'll just switch the if statement priority for left and right in the code that controls the animation direction."
But no. Don't do that. Do it when you read the buttons. Because then you only have to do it ONCE, instead of having to think about the order for everything that handles right/left in your game.
Up+Down can provide the same hassles.
(I promise I'm not picking on Super Mario Bros, it's just a game where I know this happens that isn't an unreleased indie game I'm not comfortable sharing.)
A lot of older games don't filter left+right, because... there's a d-pad. The d-pad should PHYSICALLY prevent it from happening. Here's the lesson: Never trust hardware when you're a programmer. Never trust user input. Only pass input to anything once you've verified it's clean.
Yoshi's Island doesn't filter left+right. It's part of why you can skip to the last level basically immediately:
(You can also skip some courses in Wario Land the same way.)
As I said, old games kind of had an excuse. If you are making a game that can be played with a keyboard and you are not filtering left+right,
you are making a mistake.
It's very easy. After you read the buttons, if left and right are both held (or up and down are both held), make neither of them held. You avoid so many problems this way. If you wanna go hardcore, some games take the last one pressed if both are. I am holding right. I press left while still holding right. The character starts moving left. And I am holding left. I press right while still holding left. The character starts moving right.
I did that for a fast paced Tetris game I made. The reason for doing things this way is that if a user has a separate finger on left and right, when switching directions they may press left before releasing right. This would result in dead input for a frame or two, rather than just getting left.
One of the first things I do when someone gives me a game to play is press left+right to see if anything odd happens. If something odd happens, I smell blood in the water and assume I will able to find a LOT of bugs in the game.
(Before anyone says anything: Yes! I'm totally aware the first release of the Indivisible rom has two input problems described in this post. It doesn't filter left+right, and if you press B+A in the same frame you get standing axe instead of jumping axe. I hope you can forgive this because it was finished hours before it was to be shown, and I only touched it up a little assuming I wouldn't continue to work on it.)
A user of a game can move PHYSICAL THINGS to pass information to a game. Buttons, analog sticks, whatever. Most everything a game does is based on this input. If there were no input, the same thing would happen every time. There would not be a game.
In the case of a non pressure sensitive button, there are just two options. Held down, and not held down.
Assume this was the only information you had. You could press and hold the jump button. The character would jump. When they landed they would jump again despite you not having
pressed the button again. The game only has information about the state of the buttons for the current frame, so it doesn't know if you pressed the button while the character was still in the air, or after they landed or whatever.
That anatomy of a button press is very simple. The button is held down this frame, but it wasn't held down last frame. A release is the opposite. The button is not held down this frame, but it was held down last frame.
This is one of the first things game makers who aren't specifically given libraries that differentiate between a press and a hold learn. You can see this here in slow motion:
Just so this input display is clear:
A = A button
B = B button
S = Select Button
T = Start Button
U = Up
D = Down
L = Left
R = Right
The top row is the most recent input the game has read. Each row below that is the input from a frame before the column above it. A 1 in a column means that button was held that frame. A 0 means that button was not held that frame.
The actual controller layout below the table is the raw input being provided, but the game does not get it immediately. It
usually matches, except for left+right and up+down. Just in case you're not familiar with the NES button layout, here's a gif of each button being pressed. (As well as left+right and up+down so you can see how the raw input can differ from what the game actually uses.)
Now let's talk about input buffers. I always had an idea about them, and how I'd do them. But I recently had a realization about them, and I haven't seen it described this way "out there".
It's very simple: "Any game that can detect a button press already has a one frame input buffer." A press is held this frame, not held last frame. So you need to know the state of the buttons last frame. If you had the state of the buttons from two frames ago, you could detect a press from a frame ago. If you had the state of the buttons from seven frames ago and up, you could detect a button press six frames ago.
We already know what a press looks like. With a one frame input buffer, checking for one is simple.
Code:
Is the button held this frame?
No, the button is not held this frame.
Then it's not a press.
Yes, the button is held this frame.
Was the button held last frame?
Yes, the button was held last frame.
Then it's not a press.
No, the button was not held this frame.
It's a press!
Checking further back is not much harder. If the first check succeeds, you've found a press. Otherwise, you check one frame back. Was THAT frame a press? Was the frame before that one?
You can imagine that you could just keep going back on each failure for hundreds of frames looking for a press if you had an array with all previous presses. The reason why when you have eight frames of input you can only check back seven frames for a press instead of eight is because you need to know if the button was held or not held the frame BEFORE the frame you're checking too.
So, input buffers are easy to add by themselves. Especially for me the "checking for a press needs a one frame input buffer" made them appear really easy. And if I can do it on NES, you can probably do it on something more modern.
One of the most common stated reasons to have an input buffer is so that you don't miss a jump even if you press the button slightly before you land. But I found... you could solve that without really buffering input. If A was pressed in the air, you could start a timer that counts down. If it's still non zero on landing the jump happens. You don't need to store the state of it every frame, just how long it has been since it was last pressed. It might seem like a pedantic distinction, but thinking about things this way is why I never quite... got it.
Anyway, it's easy to add, have and read from an input buffer. But they do present some interesting cases to think about that a one frame input buffer game (one that can detect a press and a hold for the current frame) don't have to pay attention to.
Imagine that on the ground, idled, if you press A and B in the same frame you get a jumping axe attack. Now imagine a game with an eight frame input buffer. I am in the middle of an attack, not in idle. I press B button three frames before the attack will end. I press A button two frames before the attack will end.
On the first frame when I return to idle, a press of both A and B are detectable. But should I get jumping axe or standing axe? If I pressed B one frame, then A the next frame in idle I would get standing axe. (A jump cannot interrupt an axe swing, which B would has started since it was pressed in idle.) It's easy to detect that the B button came first (the game had to seek back more frames to find it), but I'm actually still deciding what to do here. Currently, you do get jumping axe.
Another small challenge is that if you buffer presses, and check for buffered presses two things that use the same button close together will
always happen together without extra checks.
I knew this would happen, but made the gif to show it off. I jump close to the wall. Wall jump uses the same button as jump, and the press that started the jump is still in the buffer. This would never happen with a one frame input buffer because by the frame she was airborne, the press would become a hold and the unheld frame before is not available to check for that press.
To fix it in Indivisible NES, I modify the input buffer directly to turn the button press that triggered an action into a hold. You'll notice that it modifies all the previous frames to held (1). That's... probably not how I'd solve it on not NES. >_> Honestly it's bad for a lot of reasons.
Finally, there's this thing:
So, Ajna is facing left. She starts an axe swing in idle by pressing right and B at the same time. Before the axe swing animation is complete, she presses left+B for a frame. The code that starts the axe swing only changed the direction if a button is held when the axe swing starts, otherwise it uses the current facing direction.
So... I made it find out how far back in the buffer the press was. And check which direction was held then. Except...:
Now she presses left+B, then right after, but before returning to idle. This is similar to the standing axe vs jumping axe case above. I thought really hard about it, and decided that since the initial axe check code used to check which button was held when the swing started (in addition to some stuff I'm doing a bit later), that she should just face right.
So...:
Now it looks for the LAST DIRECTION PRESSED if any, otherwise uses the current facing direction.
There's a final weird case. If instead of pressing and releasing right, she presses and releases down... she doesn't get crouching axe unless she's still holding it when the swing starts. I thought for a bit about this. If you press and release left, you are facing left until you press right. If you press and release down, you are standing, not crouching.
There's a lot of weird stuff with deciding on crouching vs. standing axe that I wouldn't have to consider if I didn't buffer anything. I will probably use whether you were holding down on the actual frame of the B press. Even though that's inconsistent with how left+right are done. (But crouching vs not crouching does work differently than changing direction. hold vs press or hold.)
(Disclaimer: The input buffer for the game is currently much longer than it will end up for easy spotting of bugs and demonstration for this post. The smaller the input buffer, the less a lot of it matters. Consider that for a one frame input buffer, almost
none of it matters.)
That... was a long post. tl;dr: I think good input is important. Filter left+right. If the user presses a bunch of buttons in the same frame, think about what they probably wanted and make that thing happen, even if it's hard for you to do.
Yes, the user is probably not gonna press a bunch of buttons in the same frame, then release them next frame, and yes, people won't necessarily notice if they get a different direction because they themselves may not really know if they pressed left or B first in a 2/60th of a second period. But there aren't a lot of reasons why it
shouldn't work.
There has not been a lot of progress on Indivisible. I obviously... did all this input buffer stuff. But haven't done anything in a few days. I need to buffer dashing still. (It won't be as good as Real Indivisible because my buffer can't be as long.) Then I need to
fix dashing. There have been a lot of weird problems with it since I put it in
NINE MONTHS AGO. There's a lot of other weird things I need to fix with her movement.
After that, I fix some AI things. Then testing. I'll probably never get there. ;_; Until next time. o7