I was thinking to my self, if your going to make a three hit combo system should you make a timer that activates when the first button is pressed, then make a while loop stating that while the timer is still active if the button is pressed again do second and third moves? is there a better way.
Yep. I don’t think you’ll have too much success trying to do that with timers and while-loops. What you need for this sort of thing is a state machine. When a button is pressed, you just look at what state you’re in, and how long you’ve been in it, to decide what state to shift to next — which could be the next part of the combo, if the timing is right.
Check out how double jumps are handled in this article for an example.
Behaviour Tree tutorial time!
Behaviour Tree can be a solution to implement your combo system.
Let’s say you are implementing a fighting game and your three-moves-combo is:
- Right Jab
- Left Jab
- Right Uppercut
The player can perform this combo by pushing the punch button three times in a row, with the good timing.
Let’s start simple, and use the sequence node to define the combo:
sequence
WaitPunchButtonPressed
RightJab
WaitPunchButtonPressed
LeftJab
WaitPunchButtonPressed
RightUppercut
Here we wait for the player to push the punch button before executing the next move. It works, but the combo never fails, since, by definition, the sequence node executes each task, one after another, as long as they succeed, therefore the three moves will be executed, no matter what, as long as the player pushes the punch button. So we need to introduce some timeout such that if the button is not pressed quickly enough, the sequence fails.
We can use the race node to succeed if the punch button is pressed before 0.2 seconds, and to fail otherwise:
sequence
race // either wait the punch button or wait for 0.2 sec.
WaitPunchButtonPressed
Wait 0.2 // Timeout duration
IsPunchButtonPressed // Succeeds if the the punch button is pressed, fails otherwise.
We can wrap this code into a sub-tree, let’s name it “WaitPunchButtonPressed_FailOnTimeout”, as follow:
tree "WaitPunchButtonPressed_FailOnTimeout"
sequence
race
WaitPunchButtonPressed
Wait 0.2
IsPunchButtonPressed
Then we can reuse that sub-tree to modified our original sequence which becomes:
tree "3PunchCombo"
sequence
WaitPunchButtonPressed
RightJab
tree "WaitPunchButtonPressed_FailOnTimeout"
LeftJab
tree "WaitPunchButtonPressed_FailOnTimeout"
RightUppercut
And here we go, we have our combo implemented.
These behavior tree scripts are not some sort of pseudocode, they are actually functional scripts, which can be copy-pasted and used as it is. This work under the Panda BT framework, which is available on the Asset Store.
More information about this framework here:
http://www.pandabehaviour.com/
I’m the author, so if you have any question, please fire.
How did I know you were going to say that?
But seriously, a simple state machine is perfectly appropriate to handling attack combos. I know you love your BTs, but they’re not the best solution for everything. (Some would argue they’re not the best solution for anything, but I certainly wouldn’t have that argument here. ;))
Seriously, an FSM will do the job. Thought I have some doubts about the “perfectly appropriate” part. I would argue that FSM is not the best tool for everything neither.
A combo is by definition a sequence. However an FSM does not inherently support sequences; it’s possible to implement sequences using FSM but the concept of sequence is not part of the strict definition of FSM. An FSM is a set of states and a set of transitions, nothing more. Which indeed have the advantage to be simple to understand and to master, but not simple to use, practically.
Whereas BT, at its very core definition, contains the concept of sequence (among other concepts). Which makes it a more appropriate tool to implement sequences.
More generally, BT is semantically richer than FSM and its expressiveness makes the distance between design and implementation shorter. But that’s another discussion.