How can I restart a behavior tree that has already run once and completed?

pako

New member
Hi,

I have a behavior tree on an enemy, which uses the "Patrol" task from the movement pack, along with two ConditionalEvaluator tasks, configured with HasReceivedEvent. I want the tree to start running when the enemy spawns in the scene, stop when the enemy despawns and returns to its pool, and then restart when the enemy spawns again. I haven't yet figured out how to do the last part properly. So, upon enemy respawn, the enemy remains still. Apparently, the 2nd HasReceivedEvent evaluates to true, even though it hasn't be sent a 2nd time.


Behavior running well on first spawn.JPG

Above is the tree with the enemy behaving as intended, after the initial enemy spawn. The ConditionalEvaluator task on the left is set to receive the "PlayerInjured" event, upon which, it executes the StopBehaviorTree event, with "Pause Behavior" set to true. The intent is to be able to continue the Patrol Behavior if the Player revives. The ConditionalEvaluator task on the right is set to receive the "ResetEnemy" event, upon which, it executes the StopBehaviorTree event, with "Pause Behavior" set to false, which happens on enemy despawn. The PatrolCustom task derives from the Patrol task, and the only addition is the implementation of the OnBehaviorComplete() callback. I did this in an effort to fix this, unsuccessfully nevertheless.


Behavior after enemy killed and despawned.JPG

Above is the state of the tree after the enemy despawns.


Behavior after the killed enemy gets re-spawned.JPG

Above is the state of the tree after the previously despawned enemy respawns. I stepped through the code and found that the HasReceivedEvent evaluates to true, i.e. as if the "ResetEnemy" event has been sent again, only it hasn't been sent a 2nd time.

I played around with several settings of the tree and the tasks, as well as tried controlling it through code, but could not figure out how to do this.

I'd really appreciate your help.
 
The Selector Evaluator is normally used for a very specific purpose where conditional aborts cannot be used. Is there a reason why you are using the Selector Evaluator versus a Parallel task?

Beyond that I would not stop/restart the behavior tree within itself as it makes it more complicated than it needs to be. If you want to stop the behavior tree you can disable the component or pause it with DisableBehavior: https://opsive.com/support/documentation/behavior-designer/behavior-tree-component/

When the tree starts back up with EnableBehavior it will restart assuming you did not pause the tree.
 
The Selector Evaluator is normally used for a very specific purpose where conditional aborts cannot be used. Is there a reason why you are using the Selector Evaluator versus a Parallel task?

Beyond that I would not stop/restart the behavior tree within itself as it makes it more complicated than it needs to be. If you want to stop the behavior tree you can disable the component or pause it with DisableBehavior: https://opsive.com/support/documentation/behavior-designer/behavior-tree-component/

When the tree starts back up with EnableBehavior it will restart assuming you did not pause the tree.

Thank you for your response.

Actually, I had tried the Parallel task, as well as some other tasks to see what would work in this situation, but only the Selector Evaluator task worked. BTW, I'll take this opportunity to ask for clarification regarding child priority: The description of the Selector Evaluator task refers to the priority of its children, and I wasn't sure about which child had lower/higher priority than the other children. So, I shifted the children around, and finally, it worked. I had assumed that since the tree evaluates from left to right, the highest priority child would be the one that would execute first, i.e. the leftmost. However, it didn't work that way. Apparently, the highest priority child is the rightmost. Is this how child priority works or is it something else?

After your suggestion, I replaced the Selector Evaluator task with the Parallel task, and set its conditional abort to Self, then Lower Priority, then Both. In all cases, the enemy moved only to the first waypoint and stopped. Then I switched back to the Selector Evaluator task (no Abort Type), and it worked well again. Here's a screenshot of the same tree but using the Parallel task, just after the enemy reached the 1st waypoint and stopped moving:

Use Parallel Task.JPG


As I'm getting more familiar with BD I was thinking to replace the two StopBehaviorTree tasks with code, pausing or disabling the behavior as you recommend. I believe it would be more performant too. However, I got stuck with the issue I posted and didn't replace the relevant code just yet, as that particular part worked. Nevertheless, I tried restarting the tree in code before respawning the enemy:

C#:
PatrolCustom patrolCustom = behaviorTree.FindTask<PatrolCustom>();
        
        if (patrolCustom.BehaviorHasCompleted)
        {
            // Stop the behavior tree
            behaviorTree.DisableBehavior();
            // Start the behavior tree back up
            behaviorTree.EnableBehavior();
        }

It didn't work. More specifically, I stepped through the code and found that the Disable/EnableBehavior executed just fine, with the behaviorTree switching states accordingly, but the result was as I posted above (3rd image in my original post). i.e. no enemy movement upon respawn.

I also tried disabling and re-enabling the BehaviorTree component, with "StartWhenEnabled" = true. It didn't work either.

In all cases, and when stepping through code, I found that the HasReceivedEvent evaluates to true, i.e. as if the "ResetEnemy" event has been sent again, only it hasn't been sent a 2nd time, as I said in my original post.

There might be a race condition, causing this, as I send the "ResetEnemy" event in two mutually exclusive methods. I do have Debug.Log in both these methods, and only one of them gets called. However, I need to check more closely for a race condition. Otherwise, I can't imagine why the "ResetEnemy" event could be sent (or maybe registered) twice.

So, I'm still a bit stuck. I'll try replacing the two StopBehaviorTree tasks with code, to see if that makes a difference. Nevertheless, I can't see why the HasReceivedEvent evaluates to true when the relative event has not been sent.
 
The description of the Selector Evaluator task refers to the priority of its children, and I wasn't sure about which child had lower/higher priority than the other children. So, I shifted the children around, and finally, it worked. I had assumed that since the tree evaluates from left to right, the highest priority child would be the one that would execute first, i.e. the leftmost. However, it didn't work that way. Apparently, the highest priority child is the rightmost. Is this how child priority works or is it something else?
Are you running the most recent version of Behavior Designer? Version 1.6.6 changed the child execution order so it evaluates from left to right.

Nevertheless, I can't see why the HasReceivedEvent evaluates to true when the relative event has not been sent.
I actually haven't used the HasReceivedEvent task with the Conditional Evaluator so I need to look closer into that. I would try instead to use conditional aborts because the HasReceivedEvent task will definitely work with that setup.
 
Are you running the most recent version of Behavior Designer? Version 1.6.6 changed the child execution order so it evaluates from left to right.


I actually haven't used the HasReceivedEvent task with the Conditional Evaluator so I need to look closer into that. I would try instead to use conditional aborts because the HasReceivedEvent task will definitely work with that setup.
I'm currently running v.1.6.6 but I think I was in an earlier version when I started working on this BT.

I think that the main source of my confusion was that I was somehow mentally "anchored" to thinking in an "if-then -- top-to-bottom" logic rather than the "AND/OR -- left-to-right" logic. This is why I used the Conditional Evaluator configured with the HasReceivedEvent task. I was thinking: "if HasReceivedEvent THEN Stop the BT". However, the HasReceivedEvent had no connection box at the bottom to connect it directly to the StopBehaviorTree Task. So, the Conditional Evaluator configured with HasReceivedEvent task allowed me to make the direct connection to the StopBehaviorTree Task, even though, now I know, it's an unorthodox way of using it.

Thanks to your very helpful guidelines I succeeded in shifting my mindset to the "AND/OR -- left-to-right" logic. So, I created the following BT, which works well for respawning the enemy too:

Finall BT.JPG


The two Sequence tasks are configured to Lower Priority abort and everything works very well!

Thank you again for your great support!
 
Top