Using animation BP state machine for implementing a combo system

I’m intending to use an animation BP to describe the states that you can be in for a specific weapon.

This would give me 1 state for each attack with connections describing how you can construct combos.

So far this should look like a tree.

But then comes the tricky part:
from each attack state that is not invincible there would be a transition to a on-take damage state that can happen at any time.
at the end of each finisher attack there would be a transition to a long cooldown state.
and for each attack there would be a transition back to idle (if player did not give any more input).

And now we got some spaghetti madness!

So what i would do if i wrote this in C++, is add a feature of the type, “on event, if state XCur belongs to category, then begin transition to state XArg”

Is this possible at the moment?

Or do I need to roll my own with C++ & data file describing each state?

Personally I would use a montage for this rather than a state machine. Unless the way the combos work is they need to be arbitrary (i.e. Attack 1 needs to blend seamlessly to Attack 2 or Attack 3, but Attack 2 also needs to be able to blend seamlessly to Attack 1 or Attack 3), a Montage is pretty much built for this. You can think of each attack as being an animation that starts from the end of the last anim (idle for the first move in a combo, otherwise the prior attack) and ends with a back-to-idle animation, and then use montage sections to indicate transition points where queued inputs will change the ending section from “back-to-idle” to the next attack. This is how I do combos.

From a State Machine though, you could simplify things a lot by using multiple state machines; you have your standard locomotion state machine, and a second combo state machine, and just blend from locomotion’s output to the combo’s output using a Bool and a Blend Pose node in the master anim graph.

This is how I handle state transitions between my character’s multiple gameplay states and a “taking damage” state; taking damage is its own graph, and the anim graph just flips from whatever the main state machine is doing to that graph, rather than trying to connect every possible state WITHIN the graph to a “take damage” state.

I sort of like state machines as they visually show the base tree with the combos as branches.

To make things a bit more complicated, I’m thinking about having block-canceling, meaning you can go to block from most states and go to some other set of “base” attacks from the block state.

So ideally adding some kind of category tagging and having extra event rules doing things based on if it belongs to certain categories would be easiest, I believe.

But if you say that a montage is superior then i guess i should look into that.

BTW RythmScript; how do you synchronize between them so you know for sure that it is in the right state?

How do you mean synchronize between them? You mean for multiple state machines? What I do is when the player takes damage, first I set a bool that will switch the output from one state machine to the other… Then, I unset any state specific booleans (i.e. I call functions which cancel states like dashing or hovering or any other state that the player consciously enters)… From there, I just let the machine work. The player will be guaranteed to be in a grounded-idle or falling state when the blend-poses-by-bool that switches between state machines flips back.

As for Montages… Well, the thing is, elegance exists where you let it. Montages are great for combos because they can be configured easily to perform frame-synchronous matchups of animations. Typically, for combos, you don’t want to allow the player to cancel a punch partway through with the second punch, you queue the input on the hit before (so what you press WHILE Punch1 plays determines what plays immediately after it). Montages are WAY more elegant at that than State Machines because of Section operations—if you define an “End” section for each hit, you can configure a montage to default to Punch1, Punch1_End, and if Punch is pressed during Punch1 you can tell the Montage “Set Next Section Punch1, Punch2” with a node and it will automatically make Punch1 lead into Punch2 at the same frame it would normally switch to Punch1_End. These changes reset to default every time the Montage stops so the logic basically just works for combo tree navigation.

And since Montage Sections have easily gettable Name values, it’s trivial to “Get Montage Current Section” and hit a Switch-on-Name node so that whichever attack key was just pressed, that execution path can be forked based on the currently-playing animation to the desired next move (i.e.
Punch > GetMontageCurrentSection > SwitchOnName
> On Punch1, SetMontageNextSection Punch1, Punch2
> On Punch2, SetMontageNextSection Punch2, Punch3
> On Kick1, SetMontageNextSection Kick1, PunchFromKick
> On PunchFromKick, SetMontageNextSection PunchFromKick, Punch2

Etc. This makes the actual Event Graph nice and tidy, with basically no necessary variable values to set AT ALL (with a state machine, you’d need to store an Enum or Name for the current attack, set another Enum or Name for the next desired attack, and use Anim Notifies at key frames in the anim to Set the former’s value to be that of the latter to trigger the actual State Transition Rule each time) and easy to read.

The counterpoint is the Montage ITSELF is going to be an enormous mess because rather than a nice tree, each anim must be laid out more or less linearly. And you have to take care when setting up the montage (my advice is to keep as many notifies and curve values stored in the anims themselves, rather than the montage asset, as you can… And ALWAYS set your Sections to be Relative rather than Absolute) or redesigning move anims can break all subsequent anims in the Montage by misaligning stuff (I recently had to go back and manually transfer like ALL of my Montage notifies and curves for various attack stuff to child anims because I tried to extend one move’s play time by 20 frames to add a new branch and since it was the 5th move in the Montage layout, moves 6 thru 19 were broken when the curve data that corresponded to them got misaligned).

But I suppose that’s a tradeoff you have to decide on. Personally, I’d rather confine my spaghetti to an asset wherever possible and leave my Event Graph and State Machine legible if I can, since they’re already enormous messes and they don’t need any more help from me looking atrocious.

It would be much easier if there was an ability to call something like setState/beginTransitionToState and getStateCategories from a state machine to be able to interrupt it and do things based on “tags”

Well you sort of can. I mean, at any given time you’re only in one state; just use a Name variable and a NameEquals bool check for the Transition rule.

I.E. from Idle, the transition rule to Punch1 is “MoveName=Punch1”, and from Punch1, the transition rule to Punch2 is “MoveName=Punch2” and the transition rule to Punch3 is “MoveName=Punch3”.

You would just, as I said, need two Name vars. One would be “DesiredNextMove” and one would be “NextMove”; your inputs would determine what DesiredNextMove was and you could create a generic Notify which copied the value of DesiredNextMove to NextMove, so that the transition only occurs if (a) the next move is something valid (otherwise, for example if the value of DesiredNextMove hasn’t changed due to non-input, the animation just plays down to the end and then hits a different notify which resets NextMove to Idle and takes the state machine back to the beginning), and (b) the attack has reached the frame where it wants to allow the next move to trigger.

Slightly less elegant than a montage, though if you make good use of transition times you get a lot more flexibility in which attacks can lead to which.

I experimented and figured out an other way; a state machine inside a state machine state!


Note: weapon to idle are missing as this is WIP

Hi , this looks very interesting. Big fan of State Machines and Behavior Trees. Worked with Behavior Trees in UDK Kismet. Curious about the Logic in your Transitions Rules. Are you implementing Behavior Tree logic for Selectors, Sequencers, Parallel, Decorator in them?

https://docs.unrealengine.com/latest/images/Engine/Animation/StateMachines/CreatingStateMachines/NewTransitionRule.jpg

This is for the Player only right now, so no behavior Tree stuff yet.

The stuff is still work in progress, but this is what i do right now:

Sending in time remaining and the kind of attack that can be chained (1 for quick button press)

MasterState == 1 implies that it is still in UsingWeapon1 state
PendingAttack == NextType implies that you buffered up that kind of attack in the input buffer

I think I answered a question earlier you had on the forums and I was also thinking, much like RythmScript suggested, it would be easier to not use State machines…looks like you’ve made quite a bit of progress though…I’m very curious as to how it turns out…I like to keep an eye out for any DMC like things so make sure to update…

Thanks!

I would like to find experienced people to exchange ideas & experience with, so I’ll make posts so I can get suggestions for improvement and help others trying to do similar things as I do.

The test has now gone from just an idea to something that works:

The topmost state machine
There is an addition of a WeaponExit state that deals with quickly blending away the held weapon and get to a generic default pose.
The transitions from WeaponExit are set to have blendduration of 0,
These extra steps are to make sure the weapon states are reset by being fully blended out first

The weapon state machine got a new End state signaling that you have reached the end of a branch

The attack animations do have Notifies now:

  • onSubState1Passed signaling the end of the anticipation frames and the beginning of the attack (this is when the weapon can start do damage)
  • onSubState2Passed signaling the end of the attack frames and the beginning of the cooldown frames (this is when the weapon can not do damage anymore)
  • onAttackEnd signaling the end of the attack that will cause a transition to WeaponExit state if no attack is buffered up

Finally there is a PulseMoveCurve that acts a bit like root-motion and specifies the desired speed forward.