So, I'm working on a FSM that ticks on every frame. The inner workings can be simplified to this:
- The user (me) creates the States:
IdleState idle;
ChaseState chase;
DeadState dead;
// And so on...
- The user (me) creates the Conditions for the upcoming Transitions:
Func<bool> TargetOnSight() => () => onSight == true;
Func<bool> TargetOutOfSight() => () => onSight == false;
// This can also be written as:
bool targetOutOfSight => onSight == false;
bool targetOnSight => onSight == true;
// And so on...
- The user (me) creates the Transitions:
// Go from *idle* to *chase* based on the next *conditions*
AddTransition(idle, chase, TargetOnSight());
AddTransition(idle, chase, TargetOutOfSight());
// And so on...
Is quite simple to some extend, honestly.
The FSM works flawlessly. However, I've stumbled across a complicated issue (at least to me). There's a State that I need to trigger on a call. Basically, this State needs to be triggered by a variable that only changes for a frame. In other words, I'm looking to emulate the Animator's SetTrigger() behavior. We could also say that I am looking to set a State based on the change of a variable.
bool hasBeenHit;
Func<bool> ShouldRetreat() => () => hasBeenHit == true;
AddTransition(blahblah, retreat, ShouldRetreat());
The first thing that comes to my mind, are events. However, this approach would require me to literally rewrite the entire system from tick/update-driven to event-driven, isn't it?
I've found this and this. They both mention the usage of Properties (get; set;), though I couldn't find a proper way to implement them into my code. I think both of them uses some kind of event-driven approach (via Actions<>, for example).
How would I implement the behavior I'm looking for without using events?
I have nothing against events but there must be a way to do it without them. If there is definitely no way, I guess I'll have to go with events.
Thanks!
onSightwouldn't work forhasBeenHit? What's different about the activation pattern here? Walk us through what your state machine should do when this variable changes in various ways, contrasted against what it does with your current method, so we can clearly see the difference you want to fix. \$\endgroup\$onSightbecomestrueevery frame, and returns to false every frame. Same thing applies to every other condition.hasBeenHitwould work the same way, however that's not what I need;hasBeenHitneeds to betrueon one frame and return tofalseon the next one. This means thathasBeenHitwould have to reset to its original value once we have entered the desired state. However, the States are completely independent from the State Machine itself, so they can't hold a reference tohasBeenHit. \$\endgroup\$onSightstays true over multiple frames. ie. once it's set to true, that value is stable for a period of time, so if you don't react to it in the frame it happens, you have a chance to catch it later. But forhasBeenHit, the process that tries to react to it might not run in the single frame it's true, and so might miss it? Is that right? \$\endgroup\$onSightstays true over a certain period of time. We have to look at it this way: an NPC is idling, waiting for someone to approach it. When someone is near,onSightbecomes true, and since the State Machine runs on Update,onSightstays true, until that someone is too far from the NPC; That's whenonSightbecomes false, and stays false until the process repeats itself.hasBeenHitis a reaction. \$\endgroup\$hasBeenHitbecomes true only for a brief period of time, and thenhasBeenHitretuns to false.hasBeenHitcannot stay true the same asonSightbecause it's only a quick reaction, it needs to immediately return to false. \$\endgroup\$