libgdx / gdx-ai

Artificial Intelligence framework for games based on libGDX or not. Features: Steering Behaviors, Formation Motion, Pathfinding, Behavior Trees and Finite State Machines
Apache License 2.0
1.18k stars 241 forks source link

Behavior Trees: Freeze in BehaviorTree#step() in certain conditions #109

Closed MeFisto94 closed 4 years ago

MeFisto94 commented 4 years ago

Issue details

First of: I don't know whether this is just a misunderstanding or something that could really be improved. Now this is clearly a "fault" in my trees, but it would be good if step() wouldn't freeze indefinitely. See the following behavior tree: Approach will always fail (due to a problem in my code), thus InRange will be irrelevant here and untilSuccess will start to loop Approach indefinitely.

This means step() will freeze until Approach succeeds, which in turn blocks my update loop entirely.

Solutions would be an optional timeout for step() or even better, have step() only handle one iteration of a loop decorator.

Or is there something I am overlooking?

Reproduction steps/code

($notInRange) untilSuccess
    sequence
       Approach
       InRange range:2 # Re-Approach when range is broken.

Version of gdx-ai and/or relevant dependencies

'com.badlogicgames.gdx:gdx-ai:1.8.1'

davebaol commented 4 years ago

TBH I don't think it's a good idea to make changes to avoid this kind of programming errors. It would be like changing the while loop in Java by adding a timeout to remedy any condition always true. Closing this for now. Feel free to reopen if I missed something.

MeFisto94 commented 4 years ago

Well, yes and no. My proposal wouldn't only apply to endless loops but anything that takes reasonably long. I would think like having tick() only execute one leaf task (not guard) ever per tick.

I haven't thought this reasonably through but there don't have to be hard programming errors, but things that "take a while", think:

untilSuccess
   sequence
      TryUnlockDoor
      BlahBlah

I can imagine two things here:

  1. Unlocking the Door takes a few tries or it is blocking (Status.RUNNING) for 5 seconds until the door is open.

Call this one mis-design, but when executing from the update loop, the whole game is frozen and thus the animation doesn't show a door opening. But there it gets worse:

  1. When TryUnlockDoor requires update() to do something, say it starts a process and then update() is doing something every frame to open the door, until TryUnlockDoor succeeds.

Do you have another idea on how to completely bypass all these problems? Because I want my users to be able to add in behavior trees as well as I wouldn't know how to properly avoid these issues. Especially it seems like this kind of behavior depends on the "iterator" or decorator, because I am certain I already have some tasks which "block" for a few frames.

davebaol commented 4 years ago

Non sure what's your exact use case. But it's very likely that one of the several BT constructs supported by gdx-ai might cover it. For instanse, have you considered using parallel? With its policies and orchestrators is likely the most powerful and versatile construct.