Problem with Finite State Machine

I posted this issue on Discord, and talking to VegeTato, I got the impression that I had fixed the error. But I could not. It just took a little longer to happen.

What’s happening is: Every time I put several enemies in the room, for some reason they all freeze in the animation, it doesn’t happen if I put 2 or 3 only.

Look:

My finite state system works like this:
I have 3 groups of enemies gEnemy which are all enemies, gPatroller which are enemies that are walking around, and gMelee which are enemies that attack melee. (Understand groups as classes and subclasses)

In other words, my gEnemy are in gMelee and gPatroller, what decides what each group will do is the FSM.

State: IDLE

State: gPatroller

State: gMelee

I believe that using this way there is no problem, as the text of the FSM changes according to the distance that I programmed, so that each class receives its respective status.

Well actually it works really well, all the enemies I have are working 100% with the programming I made. The only problem I’m facing is this freezing of the enemy’s state.

Searching here on the forum, I found something that could be happening with my system. And removing some “trigger once” the game works and takes a little longer to freeze the animation, but even so it happens several times.

Hope someone can help me and at least give me an idea how to fix this.

Pretty sure you’re getting timing conflicts due to using generic events rather than for each events. Conditions narrow the selected object list immediately in a frame so there could be something odd there.

The majority of my FSM events for enemies use a parent ‘For Each enemyobjecthere’. The only time I tend to use generic events is if the enemy doesn’t have multiple states they can be in at the same time.

I’m using “For Each” before calling FSM

Because otherwise, everything stands still.

I’ve also split into classes, (gEnemy, gPatroller, gMelee) in their respective states.

Hmmm, I’m not sure if that’s going to do what you expect. You might need to do it within the external event sheet.

You could take a look at the DelayedChase Enemy in Not-a-vania. I did some testing by having 10 of them in the same screen and they all activate correctly/independently of one another.

Although, to clarify, I’m not doing a full FSM with numerous event sheets for that enemy, I’m doing it mostly in one external sheet. Not sure if it’ll be 1:1.

If you’re seeing animation freezing, though, that seems like the animation is finishing and there’s some logic hole that is causing it to not change states or restart. When I was building out the hero for NAV, I kept having this happen before I had conflicts between idle and running states of some kind.

A couple of things - you don’t appear to be using the FSM properly (though I could be wrong - you’ve only provided a small amount of information to go on, so we’re very restricted with the info we have and are making many assumptions).

You set the FSM states, but you seem to base action on conditions of the character - for example, “gEmeny is on floor” is not a FSM state. You should really have the parent condition be like Text of EnemyFSM = "Walk" or Text of EnemyFSM = "Attack".

Also, you are mixing the groups - in the EnemyFSMIdle event sheet, you check the gPatroller distance to hero > 50, and you also check gMelee distance to hero < 45.

And with that gPatroller and gMelee example, what happens to enemies that are between 45 and 50 pixels away from the hero?

1 Like


edit: sorry!

I relied on not-a-vania, after I did it for my character, I did almost the same procedure for enemies.

Make sure you’re checking Not-A-Vania’s current release (included in the engine’s exmaples). As mentioned in the original release it was based off not great principles. The current release uses proper state variables.

That new screenshot looks better to me as well. but yeah, as MrMen mentioned, there’s some conflicts in your events in your first screenshot (gPatroller/gMelee/etc)

I’ll give you more details tomorrow, it’s late here.

Also, you are mixing the groups - in the EnemyFSMIdle event sheet, you check the gPatroller distance to hero > 50, and you also check gMelee distance to hero < 45.

That condition is correct, each, states are all right. you can test it…

PS: Note that gMelee is activated as soon as it leaves the walk condition (gPatroller), and to avoid any problem it activates Attack, as it is already in the distance range.

To clarify: You shouldn’t merge state machines for different AI types. This is what I (and I think MrMen) am talking about. Unless you have enemies that share all of the same states, your external event sheets for gMelee should not be in the same event sheet as your gPatroller.

Object selection and variable changing can get super weird otherwise, because there’s dozens more potential conflicts that can happen. Yes this might mean you need to reproduce “like” states (such as idle), you still shouldn’t have two different logic methods in the same FSM.

gEnemy (Idle, Fall, Hit, Death)
gPatroller (Walk)
gMelee (Melee Attack)

gEnemy’s state, shares only the states (Idle, Fall, Death, Hit) already, melee attacks, and spells, have their own groups.

Groups:

gEnemy

As we know, all the enemies I have are from the gEnemy group, however, it will only do certain things that are for its status, great!

gPatroller

But one of the enemies that is in the gEnemy group, they walk from one side to the other. This enemy I put in gPatroller

gMelee

This same enemy attacks melee, group gMelee

Therefore, an enemy is in three different groups, but they share certain states.

When a gEnemy is not close to the Hero (using distance) it goes to the gPatroller state and walks back and forth, in this state it only goes to the IDLE if it is close to the Hero. However, in the IDLE state, there is a state that says that when gMelee gets close enough, it will go into combat state.

So the problem here is I don’t think your picked object in a group will transition to another object group, even though the picked object is in both. Or at least, it may not be reliable.

I’d ask for confirmation from @Bouh and @arthuro555 here because they know way more about object selection than I do.

An object type always has one object list associated. To it. When using an object group, the action/condition you use it in will simply more or less “copy-paste” the action/condition for each “real object type” behind that group (this is called “expanding the group” internally). Therefore, if your Group Y and Z both have object X, you have a condition that picks some instances of Object X via Group Y and execute actions on Group Z, that action will keep the object picking fro Object X from Group Y’s condition.

See this small example: game.json

1 Like

As I understand it, what I’m doing works really well right? And since it deals with states, the enemy variable always receives what must be done, and only leaves it if the condition is real. Well, I did a debug to understand what happens when the animation freezes.

All states work normal. Enemy enters IDLE, goes to Wak, Idle again and Attack, and back to Idle.

I was thinking it might be the time I set for each enemy to attack, so I decided to put him in IDLE state before entering Attack, the result was the same. The most interesting thing, and it seems to me, is that the programming of several objects gets lost, even taking away any object time. For example, if the enemy’s condition to get close to the player, has no execution time defined by the object’s time, it attacks without waiting (Logically), but still freezes. So I discarded object time

Is there a chance you are using a scene timer somewhere in one of your states instead of object timers?

Good to know. I was never clear so I always avoid mixing logic with object groups to avoid selection issues.

No, I don’t use it for that! If you use scene time, then yes that breaks the game once and for all. I’ve had experiences like this and I always use variable objects.

Then I’m stumped. I can’t reproduce the issue with any of the AI logic in not a vania even if I merge groups, nor can I reproduce it in my actual game with full AI FSMs, even with my bats that change what they do depending on distance.

I did a test here, with the attack state separate, not using FSM, I just left the condition that if the hero is below 40, the attack happens. And still freezes.

I would understand if it happened only to one or two types of enemies. But for example, if there are several different enemies on the screen, and one enemy is attacking me, when that enemy freezes, all the others freeze too.

Yeah, I’m inclined to believe it’s not something specific to the FSM, but it’s unclear what it could be. Could it be something with your damage events? Something that’s conflicting or perhaps the damage isn’t set to for each attacker?

I’m not sure how that’d cause issues with the animations.

(The only other thought as far as testing would be to try isolating it. Make a test copy of your project, move one enemy type into like, the GMelee group, update all of the events to use just the GMelee group, then spawn a ton of them to see if it still occurs)

Edit: Oh, the only other thing I thought of: For your transitions (or any conditions as part of your AI), if they’re checking that an animation is finished, add a condition to that event that specifies the animation too. While it shouldn’t matter (since you’re setting the animation in your actions anyway), it could help avoid any potential conflicts where it’s unsure which path to take.

As you can see, in the video, I took out the damage the enemy does to me, just to take the test. Enemy damage is the only one I use outside of FSM, because I don’t want to use “HIT” animations on them. So this event does not depend on any FSM

These transitions I had already added, didn’t change anything in the bug relationship, so I left it anyway.

I had isolated too, and left only 3 states (Idle, Attack and walk) the same happens. What I think is that there is some counting in “Finish Animation” that doesn’t seem to be happening. For example, if I remove the “Finish Animation” from the “Attack” event and put it in a global event, it works!

See the example I made and it worked:

I know, I shouldn’t do this, but just a test.