• Godot HelpProgramming
  • Does anyone have any tips for how to fix my entity FSM getting "stuck" in the chase state?

This is a long shot, I'm sure, but I'm close to my wit's end here with my entity AI. It's a general-purpose AI that is used for both enemies and allies, with a simple bool designating whether they're friendly or not.

Currently, there are these states:

  • idle (not doing anything, ready to move to either roam or follow)
  • roam (walking to random spots around the map)
  • follow (like roam, but walking to random spots near their leader)
  • chase (close in on the target, moving to ready_attack when in range)
  • attack (play the animation, then change back to ready_attack)
  • ready_attack (choose from a list of attacks based on range, then move to attack, or back to chase if not in range)
  • emerge (initial state on spawn, plays the spawn animation then moves to idle)
  • die (deletes physics nodes, plays death animation and queues deletion)

These states are all attached to basic nodes, with the main script (entity.gd) running their update() functions in process, and enter() and exit() funcitons when moving between. The AI works almost perfectly despite this glaring flaw. And the weird thing is, the enemy AIs don't seem to have the same problem - they work almost flawlessly. But upon introducing the allies, this error has reared its head.

One fix I've found in game is to kill the ally's target, thus resetting their state back to idle. I've hunted through the code to find what might be getting them stuck in chase, to no avail.

Perhaps I should also mention that there is an acquisition timer running throughout idle, follow and roam, that searches for targets and selects one when within its detection radius (an area). This could possibly lead to problems, but I'm at a loss as to how.

This (https://streamable.com/qgz0ds) shows the 'freezing' happening.

And, if you'd like a closer look, here is my repository:
https://github.com/kyzfrintin/Underworld-Wonderland

The relevant sections are under scenes/entities.

A massive thanks to anyone who would like to help!

I had a glance at your project and am unfortunately confused by the structure/abstraction between classes. It seems state changes are called in about 4 different scripts with grandchildren controlling grandparents called 'parent' and trying to track back and forth is... not for me.

Anyway, someone smarter than I may give some incite on your code, but in the meantime I found the following video to be a great explanation of one way to create/maintain state machines that I use in my Godot projects:

Also just as an aside, the check_follow() command seems under a certain condition to recursively call itself, but without updating anything that would break the loop? Unless this function is being called as a thread across frames, I can't see how this wouldn't cause a hard-lock in that condition.

So if the allies suffer the problem when more are added then your state machine may be thrashing between selecting a foe, chasing and attacking when the number of allies<>enemies gets too high. Your intuition about acquire being a possible source of your bug might be right.

You might have a problem where the state is not changing even as the target is changing constantly.

IIRC the list of collided bodies/areas are not sorted by distance.

1) You might want to track bodies/areas with enter/exit. Both area/body collision lists are subject to the same limit

Array get_overlapping_areas ( ) const Returns a list of intersecting Areas. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.

Because each AI evaluates every other AI, the overhead is going up by a square of AI in overlap. Consider adaptive detection (area) if the # of enemies gets "high". Also consider making distance calculation is done once per pair of antagonistic AI

I'd pick Areas or Bodies for detection and not use both.

2) The problem occurring in the allies just may mean that they are unlucky.

You can test by starting with as many allies as you start with enemies, then adding enemies instead of allies. (make it so enemies don't die) You may then find enemies having the problems.

Also I recommend finding a way to trigger a break point when the problem occurs. Give your player controller the ability to pick a stuck enemy (like press a button to do a ray cast when looking an afflicted AI, and use that to trigger a break point)

if(self == some_global_context.selected_target)
	print("something") #set_your_break_point_here  or call some code to turn on logging on that AI instance

Also when building a FSM system, I would always instrument some code

2 years later