Differentiate between types of key press
I'm doing a melee combo system. Initially I had a bunch of ifs, switches and bools, and it was getting unmanageable, So I decided to scrap that approach and use a FSM-based one instead. I have an input class where keys are mapped to constants (to allow user customisation later), this class also has a function called "GetCurrentInput()" that was intended to return the current input.
Basically my problem is that I want to be able to differentiate between when a key is pressed, when it's held down, and when it's double-tapped. Initially I looked at GetKey() and GetKeyDown(), but these will both return true on the first frame the key is pressed, so it won't differentiate between a press and a hold. Next I tried to do my own custom GetKey by saying: On key down, start a counter, if key up during that time then it's a press, else it's a hold. But then I had the problem that I'm calling GetCurrentInput() every Update(), meaning that I can't really use timers because by the time they're finished, the next Update()s will have already occurred. Another issue is that this approach implies that we can only ever have one input at a time, whereas I want to be able to have e.g. directional attacks (so holding a direction AND pressing attack).
So currently I'm looking into events and trying to only send an input when we've fully determined what it actually is (press, hold or double-tap), but my concern is that it will lead to a noticeable delay between the time the player presses a button and the attack happening, e.g. if we check for double-tap by saying: If keydown, then keyup, then keydown again within some time frame (e.g. 0.3s) then it's a double-tap, but we can't just wait 0.3s every time the player presses a button. I thought maybe instead the "wait" could happen inside the attack action itself (so the wait is still there but it's hidden by the currently executing action), or maybe have some kind of input queue/list, where we always check for the highest priority input (double tap > hold > press), even if we've already received some other input.
Answer by Nephtis · Mar 18, 2018 at 06:34 PM
Ok, I have a solution:
I basically say: For the first attack in a combo (i.e. on keydown), trigger it immediately, because we don't care whether it's a press, double-tap or hold - the first attack in the combo is always the same. For all subsequent attacks in a combo, when we receive a keydown event, DON'T immediately fire the next attack, instead wait for a short duration (currently 0.2s) and use this time to determine what the input type is.
For this duration, we keep track of our current input type, which starts off as "NONE". If we receive a keyup, we change it to "PRESS" (i.e. single tap). If we then receive a subsequent keydown, we change it to "DOUBLETAP". If however we did not receive any keyup or keydown after the initial input, we change it to "HOLD". Then, once the duration is complete, we pass whatever our current input type is to the attack action.
(So it's initial_press + release = PRESS, initial_press + release + another_press = DOUBLETAP, intial_press but no release = HOLD).
To hide (from the player) the fact that there is this delay, we do this check while the current attack animation is playing (and for a little bit afterwards), so the player can effectively "queue" their next input while the current attack animation is playing, and we won't execute the next attack in the combo until the duration is over and we have our input type.
This is just what works for now, there might be better ways of doing it.