Simultaneous Inputs & Button Timing Buffering for Mobile
Although the topic of timing and controls is generally reserved for fighting game conversations, we had some unique behind the scenes adjustments lately that we wanted to create a resource for.
We are porting some of our NES games with a partner to distribute on the App Store & Google Play. Rather than having the games recreated and mobile-optimized, we’re using a custom emulator that, when complete, will allow us a near drag and drop solution for releasing them through these digital storefronts in the future.
This might look familiar to some of you!
For those of you that have played Log Jammers, you know it’s a fast-paced arcade sports game where timing and precise controls are critical for success. Looking at the image above, you’ll see that this game includes local co-op play, something we thought would be fun for mobile as well. After some iteration and community testing on timing, inputs, and an increase in the total area that you can touch and interact with, we had one final adjustment to make: simultaneous button press timing.
In many games, two face button presses are required. In Almost Hero, as an example, A+B is what unlocks the jump kick, which is arguably the most important single move in the game.
By default, the custom emulator we were using does allow an A+B press, the timing just required exacting precision that created a mediocre gameplay experience. Mobile games should have an additional eye towards accessibility, and this type of frustrating control requirement without a reward won’t be leaving anyone in mobile's general addressable audience feeling excited.
Simultaneous inputs are possible without making any modifications to the emulator but are extremely difficult with touch controls. The strict timing is particularly noticeable because pressing the fingers down evenly lacks the physical feedback of button switches, which greatly helps in achieving truly simultaneous inputs.
Buffering can be used to simulate this physical feedback by adding a delay for singular A/B presses (e.g, 50ms) and waiting for the other to be pressed. If the other is pressed at any point within the 50ms buffer window, they are both immediately submitted to the emulator at the same time. If the other button is not pressed within the buffer, just the one that was pressed is sent to the emulator.
Software-level buffering like this is common, especially in fighting games, but even a very slight additional delay on top of the existing native delay from the emulator may have been too much.
Without physical buttons or the use of a buffering solution, the difficulty of true simultaneous inputs, therefore, depends on the game's frame rate. The game has to receive both touches on the same frame for it to be registered as a true simultaneous input. If one button is even 1 frame off from another, it means the input systems will receive one button first and start acting upon that, causing it to not use the next button on the next frame (assuming another in-game action was started by the first button). This is assuming the emulated game code itself does not use a buffering solution on the inputs it receives, in which case a buffer at the emulator layer would not do any good.
A phone I was testing on was somewhat laggy, running at about 30 FPS, which means 1000ms per sec / 30 frames per sec = 33.3 ms (per frame) window for simultaneous inputs. I can get simultaneous inputs around 50-75% reliably at that frame rate. If someone was running the game smoothly at 60fps, they would have a 16.67ms window to perform a simultaneous input, which is unreasonable and makes getting a valid simultaneous input impractical.
To improve upon this foundation, we added an optional input buffering system into the emulator. This buffering system is easily customizable on a per-game basis (including whether it's used at all). Any games which depend on a lot of simultaneous inputs would have it turned on and tuned to a value that feels like a good compromise, and games with no simultaneous inputs would have it disabled.