Jump Input Buffering
Yesterday I updated Super Ninja Slash with a few tweaks, including the addition of jump input buffering. I talked about this a little bit on Twitter, but due to the 140 character limit I think I might've caused some confusion as to what I meant by jump input buffering.
First what I mean by input buffering is allowing the player (or any source of input) to input commands into the game and allow those commands to be effective for a short window of time after they are inputted. In the case of jump input buffering, the goal is to allow the player to input the jump button and still have a successful jump even if they mistimed their input by a tiny amount.
I made some doodles to try and illustrate what exactly is going on. Imagine that this is some typical platforming game.
That pink box is the player descending from a jump that happened a little while ago. Imagine that in this scenario the player wants to immediately jump again after they touch the ground, so they try to push jump as soon as they touch the ground...
They push the jump button here! Oh no, they actually pushed the jump button right before they touched the ground. This is a problem because in this example, my platformer jumping code is basically just "if player is on ground, and player pushes jump, then jump!" Since they are not on the ground, that code won't fire. Instead of jumping, they land on the ground like so.
This sucks! As far as the player can tell, they pushed jump at the right time. With the game running at 60fps, they would have to hit a frame perfect timing of 1/60th of a second to actually jump on the very first available jump frame. If this were a game that had a big speed running element, then this would be pretty tough to consistently pull off, and I don't want the player to have to grab a frame perfect input just to jump as soon as they can.
To fix this I can buffer the jump input. This is just fancy speak for having a short timer variable that acts as the jump input instead of a boolean. When the jump input is pressed, set a timer to an amount of your choice. In the case of Super Ninja Slash, it's 6 frames of buffer time. When that timer is greater than 0, count it back down to 0. Now instead of checking to see if the player has pressed jump, I check to see if that timer is above 0. I also set the timer back to 0 if the player is not holding down the jump button at any time. If the player successfully jumps, I set the timer to 0 as well.
With this new system, the example now looks like this.
Awesome! The player now jumps again as they would expect in their brain. The key of making this work well is to choose a timing window small enough that the player does not really notice the helping hand that the game is giving them.
I make use of this technique in Offspring Fling to help make speed running levels a little bit easier. When playing Offspring Fling I found that the best runs of levels were when the player could jump on the very first available frame of a legal jump, however the timing of hitting that frame perfect was way too tough to get consistently. The jump input buffering helped a lot and in my opinion made speed running levels a lot more fun and consistent.
Jump input buffering is NOT when you jump again simply because the player is holding jump. You definitely want to have a small timing window for this, and only jump again if the player has PRESSED jump again. If you simply jump again while the player is holding the jump button, it can feel pretty goofy mostly because the jump action is now disconnected from the input. You want to avoid feeling that disconnect when adjusting your timing window for the buffering.
I hope this clarifies my tweet a little bit!
First what I mean by input buffering is allowing the player (or any source of input) to input commands into the game and allow those commands to be effective for a short window of time after they are inputted. In the case of jump input buffering, the goal is to allow the player to input the jump button and still have a successful jump even if they mistimed their input by a tiny amount.
I made some doodles to try and illustrate what exactly is going on. Imagine that this is some typical platforming game.
That pink box is the player descending from a jump that happened a little while ago. Imagine that in this scenario the player wants to immediately jump again after they touch the ground, so they try to push jump as soon as they touch the ground...
They push the jump button here! Oh no, they actually pushed the jump button right before they touched the ground. This is a problem because in this example, my platformer jumping code is basically just "if player is on ground, and player pushes jump, then jump!" Since they are not on the ground, that code won't fire. Instead of jumping, they land on the ground like so.
This sucks! As far as the player can tell, they pushed jump at the right time. With the game running at 60fps, they would have to hit a frame perfect timing of 1/60th of a second to actually jump on the very first available jump frame. If this were a game that had a big speed running element, then this would be pretty tough to consistently pull off, and I don't want the player to have to grab a frame perfect input just to jump as soon as they can.
To fix this I can buffer the jump input. This is just fancy speak for having a short timer variable that acts as the jump input instead of a boolean. When the jump input is pressed, set a timer to an amount of your choice. In the case of Super Ninja Slash, it's 6 frames of buffer time. When that timer is greater than 0, count it back down to 0. Now instead of checking to see if the player has pressed jump, I check to see if that timer is above 0. I also set the timer back to 0 if the player is not holding down the jump button at any time. If the player successfully jumps, I set the timer to 0 as well.
With this new system, the example now looks like this.
Awesome! The player now jumps again as they would expect in their brain. The key of making this work well is to choose a timing window small enough that the player does not really notice the helping hand that the game is giving them.
I make use of this technique in Offspring Fling to help make speed running levels a little bit easier. When playing Offspring Fling I found that the best runs of levels were when the player could jump on the very first available frame of a legal jump, however the timing of hitting that frame perfect was way too tough to get consistently. The jump input buffering helped a lot and in my opinion made speed running levels a lot more fun and consistent.
Jump input buffering is NOT when you jump again simply because the player is holding jump. You definitely want to have a small timing window for this, and only jump again if the player has PRESSED jump again. If you simply jump again while the player is holding the jump button, it can feel pretty goofy mostly because the jump action is now disconnected from the input. You want to avoid feeling that disconnect when adjusting your timing window for the buffering.
I hope this clarifies my tweet a little bit!
Comments
Just a question: you said that you set the timer at 6 frames time.
Wouldn't it be better to set it in a time-dependent/frame-independent fashion?
So, no matter the speed of the computer you only have that "grace time" to jump.
Thanks for this. Blew my mind!
So good :D
As it's using delta time I did a lot of testing and settled on 100ms buffer time, which is spookily spot on for 6x frames at 60 fps.
Post your comment!