Finite State Machine Multiple Instances

I’m trying to create the skeleton of a Finite State Machine (FSM). I’m trying to start very simple. I have two instances of an object called Pawn (with different animations: Pawn01, Pawn02, one text variable called: State, and the Draggable Behavior). I also have one textbox named txtDebug. The Pawns have two states: Idle and Drag.

  • Idle works when first entering the program, printing both Pawn states as expected.
  • Drag works when dragging a Pawn, printing as expected.
  • Dropping the pawn prints as expected.

BUT the transition from Drag to Idle doesn’t seem to trigger the Idle action again to print. (Ex: Assuming Pawn01 was dropped, it should print “Pawn01:Idle” after the Dropped message, but doesn’t).

Main (Events):

FSM:

FSM_Idle:

FSM_Drag:

It may have to do with 1) How it repeats for each instance of Pawn once a Pawn has been selected for Dragging, or 2) the placement of Trigger Once. Any help would be greatly appreciated!

For clarification and simplification, here is the code all in one routine:

Note that you can drag and drop either Pawn and get the appropriate Drag txtDebug information (i.e., “Pawn02:Drag” then “Pawn02:Dropped”), but even though the State DOES revert back to Idle (you can see it in the Debugger, or by taking off the Trigger Once and dragging-dropping quickly), it never runs the Idle’s txtDebug information (i.e., it should read "Pawn02:Idle) after the first time through. Very strange.

1 Like

Hey, Alex here!

Hopefully I can explain what’s happening here. Your Variable “State” should be working as you’d assume; however the issue I see is in how you are managing your For Each. I don’t believe it should be needed here.

Some info to know:
In Gdevelop, Objects added to the game scene are individual “instances” of that object. I like to describe it as "Objects in Gdevelop are self-governing, meaning each instance of that object is self-aware of its own “self” and its independent variable. In short, two object instances can have different values.

When using a “For Each Object” statement, you are declaring the engine to count a number of objects and apply the actions accordingly to each of them.

continued:
Since an object is “Self-governing”, you can simply check IF the object Pawn.State = “DECLAREDSTATE”; you won’t be needing a for each, as the condition being checked is a text/string. You may need to use a for each object later on, depending on how you want / need to manage an object. However, I would suggest adding an ID to each object or bool if you are planning to have a multiplayer game.

I hope this helps,
Happy Game Making!

Hey Alex, thanks for responding!

Unfortunately, taking out the For each instance of Pawn doesn’t solve the problem. It still never goes back to run the idle state of the affected Pawn. Here’s the revised code…

As you can see, the first line under Pawn = “Idle” is:
Trigger Once, then set the text to the Pawn name and Pawn state. (I add to the existing text to make sure I see the chain of events.)

There’s something odd going on here. The same code works when State = “Drag”, but doesn’t when State = “Idle”. (except for the first pass through when the game starts, then Idle writes to txtDebug properly).

Please experiment with this if you get the chance. I’m confident you will find the same problem.

Thanks,
Tom

BTW, this code DOES work as expected IF you only have one instance of Pawn on the screen.

  • It prints “Pawn01:Idle” on running the scene.
  • Then “Pawn01:Drag” when dragging,
  • Then “Pawn01:Dropped” when dropped,
  • Then “Pawn01:Idle” again.

image

That’s what it SHOULD do!

But with two (or more) Pawns, it never prints the “Pawn01:Idle” after being dropped.

image

I think that’s because it’s behind the trigger once. It’s been triggered, there are no other conditions for the trigger once to work with, and so it won’t get triggered again.

A method I use to trigger something whenever the state changes is:

image

So the first event is triggered once every time the player state changes to “ReadingSign”, while the second event continually is processed while the state is “ReadingSign”.

And unless more than 1 pawn can be dragged, there’s no point in iterating over them with a “Repeat for each Pawn”.

HTH

1 Like

Thanks MrMen for your reply!

Your method for Player works just fine for ONE player. But when you have multiple instances of Player (or Pawn in my case), Trigger Once gets funky.

With more than one Pawn,

  1. when I enter the scene, the Idle State’s Trigger Once writes to the textbox as expected.
  2. when transitioning from Idle to Drag, the Trigger Once works for Drag.
  3. The Pawn’s State variable IS reset back to Idle (as seen in the Debugger) after being Dropped.
  4. But even though the variable has been reset from Drag to Idle, the Idle State does NOT trigger its textbox command.
  5. However, pick any Pawn to drag again, and the Drag State Trigger Once textbox command triggers, resets state back to Idle, but never fires the Idle Trigger Once (even though the variable has definitely been reset). Thus, when you say, “It’s been triggered, there are no other conditions for the trigger once to work with, and so it won’t get triggered again.” This does NOT seem to be the case for the Drag State, which triggers every time.

So Trigger Once works as expected for, A) Only one instance of a Pawn, and B) for the Drag State but NOT the Idle state when multiple instances are on scene.

(And I understand the distinction between your two examples, but I’m definitely trying to get Trigger Once to work intuitively here.)

I have made a work around this morning, but I’d really like to understand why Trigger Once only works for Drag and not Idle with multiple instances.

Mystery Solved?!?

I just came across this discussion thread, which seems to explain the Trigger Once issue…

It’s obviously a known problem when dealing with multiple instances of objects using Trigger Once, and I just happened to stumble across this thread. (Much of the discussion is above my paygrade at this point, but the gist seems relevant.)

My work around solution is to have another object variable called Transition, which is a boolean that flips to True after every state change of the object. Then, in the body of code that’s to be run once, it flips to false at the end. (This solution prints the Idle state as expected in my example.)

My technique moving forward will be to use Trigger Once when working with a single object, and a custom object variable (like Transition) when dealing with multiple instances.

Hope this helps someone else down the road!

Seems I have to change my understanding on the Trigger once as a sub event. Although logically I see how it works, it doesn’t sit well intuitively. I should have read the link below a bit slower and taken it in :expressionless:.


So here’s my take on why it’s not working when you have multiple pawns. First off, here’s an explanation of how GDevelop implements the Trigger Once. Understanding this is important to understand the why it behaves the way it does.

If you have multiple pawns, one always has the State set to “Idle” which means that first event (Pawn.State = “Idle”) is always being actioned. As such, the Tigger Once UID stays in the storage, and isn’t actioned again event when another pawn’s state changes to idle. Those pawns already have an State of “Idle” prevent that.

However, the only time the State is set to “Drag” is when the pawn is dragged. If it’s not dragged (i.e. it has never been dragged or it was dropped), then the State of a Pawn is “Idle”. When you drag, the trigger once is activated because there was no pawn with that state for quite a few game frames and the Trigger Once UID is removed from the storage.


It’s not so much a problem with trigger once, it’s understanding the logic behind it. It is logical, but not intuitive. And that’s what throws most users. From a quick skim read, that GitHub thread is about adding a more “instinctive” Trigger Once implementation.

Trigger Once is not designed to be used with multiple instances. The documentation (which I read after the fact, though to be honest, I would have probably not understood the implications without first encountering the problem for myself, or seen a detailed example) reads…

After reaching out to him, arthuro555 suggests:

image

This obviously adds another variable to track when changing states, but otherwise seems fairly elegant and intuitive.

Here is my revised code with the new implementation, for anyone interested…

While I started this journey trying to make a FSM work across multiple instances, the problem I encountered was not fully understanding Trigger Once. I feel that this discussion should have been titled Trigger Once Workaround with Multiple Instances, or something similar. Too late now.