Counting loops on a given state in state machines

Hello!

I have a state machine that is designed to give artists control of the animation behavior for non playable characters.

One of the controls I am giving artists is to let them define how many times the character will be able to loop on a given state before proceeding to the next step in the animation.

I found a way to do it that works, but it’s also been the source of many headaches and issues over the years, which leads me to think that there is probably a better way to do this.

Here’s how I do it (see attached image):

  • We enter Idle1 which triggers the Idle1_IN event where I increase my loop counter by 1
  • When the anim is done playing, if we haven’t reached the max number of loops, we transition to Looper1
  • Looper1 does nothing but stay on the last frame of the last animation that was played
  • The second transition is always True so Looper1 immediately transfers back to Idle1
  • Entering Idle1 once again triggers the Idle1_IN event which increases the loop counter by 1 again
  • … etc.

TL;DR:

I am using a temporary state (Looper1) just to that I go out and back in of my Idle1 state so that I can use the Idle1_IN event which is what I use to increase my loop counter by 1

Is there a better, simpler way to do this?

Thanks!

Hi, there’s no native or blueprint API to do this since we don’t track the number of times that an animation has looped in the Sequence players. But you could do this by storing some state data and then using an anim node function to increment it and another one to reset the data when entering/returning to the state.

I mocked something up quickly that I think should give you what you need. The code for the anim node function would look like this. I’m using a custom struct to store the state data, but you could just hold the variables directly on a custom anim instance, or just on your anim bp if you prefer:

`// .h
USTRUCT(BlueprintType)
struct FTransitionTrackerData
{
GENERATED_BODY()

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = “Transition”)
uint8 TriggerAfterNumLoops = 0;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = “Transition”)
bool bShouldTransition = false;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = “Transition”)
uint8 LoopCount = 0;
};

UFUNCTION(BlueprintCallable, Category = “Animation|Sequences”, meta = (BlueprintThreadSafe))
static ANIMGRAPHRUNTIME_API void TriggerTransitionOnLooped(const FSequencePlayerReference& SequencePlayer, UPARAM(Ref) FTransitionTrackerData& StateData);

// .cpp
void USequencePlayerLibrary::TriggerTransitionOnLooped(const FSequencePlayerReference& SequencePlayer, FTransitionTrackerData& StateData)
{
SequencePlayer.CallAnimNodeFunction<FAnimNode_SequencePlayer>(
TEXT(“HasSequenceLooped”),
[&StateData](const FAnimNode_SequencePlayer& InSequencePlayer)
{
if (InSequencePlayer.GetDeltaTimeRecord()->GetPrevious() > InSequencePlayer.GetAccumulatedTime())
{
++StateData.LoopCount >= StateData.TriggerAfterNumLoops ? StateData.bShouldTransition = true : StateData.bShouldTransition = false;
}
});
}`The idea is that in the struct, you can specify how many times you want the counter to loop. Then the anim node function will set bShouldTransition to true once you hit that number of loops. You can then use that to trigger the transition.

Your anim blueprint setup would then look something like this. First the Sequence Player:

[Image Removed]The OnUpdate function would call your library method:

[Image Removed]And the OnBecomeRelevant function resets the state data:

[Image Removed]Then you can base the transition to your other state based on bShouldTransition in the state data:

[Image Removed]You should then get a behaviour that looks something like the following. In this case, I’ve set the state data to allow the walk animation to loop two times, then we transition to run. The transition from run to walk just uses the automatic transition rule to trigger when the run animation finishes. We then transition back to walk and the OnBecomeRelevant resets the state data so we repeat the process.

[Image Removed]Let me know if you want to discuss this in more detail.

Sure, so you would likely need an engineer to help out with this. But the code implementation should be trivial. You would add this function to a blueprint library class - they allow you to expose native functionality to blueprint and/or python that isn’t accessible in the vanilla engine. In the example I sent over previously, I used USequencePlayerLibrary which is an engine class, but you should be able to have a new blueprint library class added within your project (or re-use an existing one) into which you can add a function similar to TriggerTransitionOnLooped that I shared. There’s more about bp library classes in this [knowledge base [Content removed]

The way that I set things up in that code - with the struct to hold the data, etc - is just one way to do it, but there are various other ways you could achieve the same thing in code or blueprint. The important thing is that you need to be able to access GetDeltaTimeRecord()->GetPrevious() and GetAccumulatedTime() on the sequence player to know that the animation has looped. Note that you would also probably want to deal with the case where the animation is playing backwards.

In terms of the three questions that you asked, could you give me more information on what the use case is? For instance, are you trying to add variation into the idle by having the number of loops change each time? It’s a somewhat uncommon request, which is why the information you need isn’t exposed by default (but there are many other uncommon use cases that we also don’t cover with the existing API so this isn’t the only one).

Yeah, that makes sense, thanks. And what you’re trying to do seems completely reasonable.

I don’t think there’s a better way to do what you want without some kind of code customizations. The basic problem is that we don’t track the number of times an animation has looped on a sequence player. So essentially, you need some logic somewhere to:

  1. Know when the animation has looped (using GetDeltaTimeRecord()->GetPrevious() and GetAccumulatedTime())
  2. Store that as state data somewhere (this could be in the custom struct I included in the example, or maybe on a custom sequence player node but that’s a more invasive approach since it requires more changes than a custom blueprint library implementation)
  3. Trigger a transition based on the looped value stored in the state data (this could be based on a bool set by a function, like in my example, or it could be custom transition logic added to the state machine code - again, more invasive)

So there are many ways that you could do this. But the custom logic in a blueprint library method is probably going to be the least invasive vs custom animation node and/or custom transition conditions. Another option entirely would be to do this with notifies that would mark the end of each idle animation, but again I wouldn’t recommend that since it would be error prone if someone left a notify off of one of the sequences.

Thank you very much for all the details and walking this through with me Euan!

I will look at all these options and figure out the best way to move forward on our end.

Cheers!

Sounds good. In that case, I’ll close out this thread for now. You can always reopen it later if you have any further questions.

Hello Euan!

Thanks for the answer! I need a bit more help to try this out.

I’m not super familiar with coding in Unreal so I don’t really know how to implement this anim node function that you made above.

Is this something that I should be able to do just by myself in the editor or should I request the help of an engineer on the project?

Also, given that you are confirming that there is no “out of the box” way to do specifically what I am trying to achieve with state machines, I guess the questions on my mind are:

  • Am I not using the right tools to control my NPC behaviors?
  • Was it a wrong choice to use state machines to achieve what I was trying here?
  • Are there other tools in Unreal that would be better suited to craft NPC behaviors?

Thanks again!

Again, many thanks for the reply. All of this is really insightful.

About my questions, the context is this:

I have made a Template Anim BP which contains this state machine.

We want this template to cater to 80% + of all our NPCs in the game.

When the designers creates a new NPC, they create a child of this template and they get to slot in various animations for, say, Idle1, Idle2, idle3, and a bunch of other contextual animations. We want to be able to tell a story with this template.

An example of this would be:

NPC_A starts on idle1 when the game is launched. He sticks to Idle1 (say, just standing there and looking around) until it has looped 8 times and then proceeds to Idle2.

In Idle2, NPC_A will be playing a “cheering” animation until it has looped 5 times and he then proceeds to Idle3.

Idle3 will on play a single time and will have NPC_A exiting themselves and walking out of player’s sight.

So, in a nutshell, what I am trying to do is simply to give the control of how many times each animation plays to the designers of the NPCs so that they can control the flow of that story.

Hopefully that makes sense?