Doubts with abort + reused conditional

Flow Fire Games

New member
Hi all,

I have a behaviour tree (actually it is composed by 4 external BT refs), and I want to achieve something very simple, but I cannot figure out how exactly. This is my BT:

bt.PNG

Basically it has like 4 subtrees (they are external BT's), from left to right, "Flee", "Combat", "Search", "Idle (patrol)". The thing that I want to change here is on the Flee. I just want to do the Flee task, and only if the condition of "Is Any Hostile Visible" (used also in the Combat BT) subtree is true, then I also want, in parallel, look at the target and shoot him. I tried an approach like the one that I have with the Is Low HP (with a sequence that aborts) but the aborts or either don't abort only the LookAt and Shoot nodes, or they abort parts of the BT with less priority (Combat, Search or Idle).

How should I do to make it work as intended? And by the way, if you see anything that I should change in general, let me know, I want to improve it as much as possible :)

Thanks!
 
Hi all,

I have a behaviour tree (actually it is composed by 4 external BT refs), and I want to achieve something very simple, but I cannot figure out how exactly. This is my BT:

View attachment 3720

Basically it has like 4 subtrees (they are external BT's), from left to right, "Flee", "Combat", "Search", "Idle (patrol)". The thing that I want to change here is on the Flee. I just want to do the Flee task, and only if the condition of "Is Any Hostile Visible" (used also in the Combat BT) subtree is true, then I also want, in parallel, look at the target and shoot him. I tried an approach like the one that I have with the Is Low HP (with a sequence that aborts) but the aborts or either don't abort only the LookAt and Shoot nodes, or they abort parts of the BT with less priority (Combat, Search or Idle).

How should I do to make it work as intended? And by the way, if you see anything that I should change in general, let me know, I want to improve it as much as possible :)

Thanks!

If I understand correctly, you want to Flee on low hp, and if enemy visible also attack. You can achieve this by adding selector under two main branches:

1111.PNG

Don't mind task names, this is just to give you an idea. This tree will always reevaluate two conditions: Enemy Detected and Is Low HP.

As for things you need to change - your Search branch makes no sense:
- both conditions inside the branch are identical
- sequence on the right has abort of type lower priority, but there are no tasks with lower priority.
 
It looks like your condition is going to abort the flee task as well the other tasks under the parallel. You should restructure your tree so the flee is no under the parallel task.

For a general behavior tree strategy I recommend taking a look at this page. This is useful even if you aren't using our character controllers: https://opsive.com/support/document...er/integrations/opsive-character-controllers/

Hey, thanks for answer! Maybe I should explain the exact behaviour that I want to achieve:

1.- If no one is detected and neither I know the last position of an enemy/player (which resets after some time in a detector class that it's outside the BT), then I do patrol.

2.- If I detect anybody, hearing him or seeing him (for now hearing is not implemented, even if there is a node with a last heard hostile position, it's dummy for now), I follow, look and shoot at him.

3.- If I lose the visibility of the enemy, I want to search for him in the last known position, and if I detect him again, do again the point 2 (follow, look, shoot).

4.- If I am low HP, I want to flee away. If while doing this, I have an enemy detected, I want to look at him and shoot him at the same time that I flee away.

Given this, are you sure that Flee shouldn't be under the parallel?


If I understand correctly, you want to Flee on low hp, and if enemy visible also attack. You can achieve this by adding selector under two main branches:

Don't mind task names, this is just to give you an idea. This tree will always reevaluate two conditions: Enemy Detected and Is Low HP.

As for things you need to change - your Search branch makes no sense:
- both conditions inside the branch are identical
- sequence on the right has abort of type lower priority, but there are no tasks with lower priority.

Thanks a lot for the example! Yes, you understand correct, that's what I want to achieve. My "problem" with this structure is that it came from a state machine where all this small behaviours were 1 single external behaviour, but now they are unified, so I have to deal with the full behaviour at the same time, but maybe it needs a full restructuring, I will try your approach and see if I can make it work.

Also, about the other comments, the conditions are slightly different, one is for audible detection and the other for visible, but both could be in the same node logic, yep. EDIT: okay I see that I was using the same node, that's a mistake, it's supposed to be another conditional). Anyway, I think I might have blind or deaf enemies, so for now I think I will leave them there. The abort condition, now that you mention it, you are right, it makes no sense, I will remove it.

And for more info about my nodes (maybe it helps understanding more this, and also to know which composites to use), LookAt, Follow and Shoot always return running, they never ends, as I want to control the shoot/not shoot from the tree itself and it's conditions, but honestly I don't know if it is a good practice or not x).

Thanks both for answering!
 
Last edited:
1.- If no one is detected and neither I know the last position of an enemy/player (which resets after some time in a detector class that it's outside the BT), then I do patrol.

2.- If I detect anybody, hearing him or seeing him (for now hearing is not implemented, even if there is a node with a last heard hostile position, it's dummy for now), I follow, look and shoot at him.

3.- If I lose the visibility of the enemy, I want to search for him in the last known position, and if I detect him again, do again the point 2 (follow, look, shoot).

4.- If I am low HP, I want to flee away. If while doing this, I have an enemy detected, I want to look at him and shoot him at the same time that I flee away.
Your explanation sounds perfect for three different branches using a lower priority conditional abort, similar to the Ultimate Character Controller integration tree. You could have flee under a parallel task in that case, something like:

Selector
LowHP -> Flee CanDetect -> Shoot LoseVisiblity -> Search Patrol
LookAt/Shoot

In this case all of those condition tasks would be under a Lower Priority conditional abort.
 
If I understand correctly, you want to Flee on low hp, and if enemy visible also attack. You can achieve this by adding selector under two main branches:...

I tried your approach and it using normal parallels (I don't know where that Parallel fixed comes from D: ), and it works, but I changed the right part to remove the Look Around (which for me I guess it is the Search subtree that I have) and the parent parallel, so it looks like this:

Capture.PNG

What I am missing now is the search, which needs a condition (KnowsLastHostileVisiblePosition), and if it's true, I should go check that position. After I arrive there, wander for X time. In the original picture I was missing the Wander node after the sequence with the lower priority abort (that's why the abort was there, but I forgot to add the node to this new BT that I was creating). For short, that third subtree in this next picture is what I need to restructure now:

Captur2e.PNG


Your explanation sounds perfect for three different branches using a lower priority conditional abort, similar to the Ultimate Character Controller integration tree. You could have flee under a parallel task in that case, something like:

Selector
LowHP -> Flee CanDetect -> Shoot LoseVisiblity -> Search Patrol
LookAt/Shoot

In this case all of those condition tasks would be under a Lower Priority conditional abort.
Thanks again! Could you elaborate a bit that selector branch that you wrote there? I am a bit confused about the formatting ?. Anyway, tomorrow I will check the Character Controller integration tree and see if I can do it with that approach too.

Thanks both again :)
 
I tried your approach and it using normal parallels (I don't know where that Parallel fixed comes from D: ), and it works, but I changed the right part to remove the Look Around (which for me I guess it is the Search subtree that I have) and the parent parallel.

...

What I am missing now is the search, which needs a condition (KnowsLastHostileVisiblePosition), and if it's true, I should go check that position. After I arrive there, wander for X time.
I don't have the pack that you are using, so I'm not quite sure what tasks in your tree exactly do. I use my own custom tasks for everything, including separate actions for movement and sight. These actions always return status running, they don't check any conditions internally, they are fully controlled by external conditions and aborts. I prefer this approach, but it can be done the other way. "Parallel Fixed" is also a custom task, if you have no issues with native parallels, just use them and don't bother)

As for search behavior, it's more tricky than patrol/attack/flee, because you must consider complex cases. What if character, while searching for enemy, is alarmed by another enemy? You need to restart the search branch with a new timer. There is no way to manually restart the branch, so you need to restart the timer. I can demonstrate how it's done in my tree, but it requires some code.

However, if you have only a single player and enemies don't fight each other, than it's much easier. I would make something like this. But again, I don't know if your pack already contains default solution for this behavior.

111.PNG

I assume you have a bool and a Vector3 variables for Last Enemy Position (LEP). So, if LEP is known (bool), we move there (Vector3). As soon as LEP is reached, we wander in the area, until in X seconds "Clear LEP" task cancels the whole search branch (set bool = false). Note that the sequence under selector should only abort lower priority tasks.

PS: You may want to add "Is Low HP" condition to the search branch, inverted. So that agents on low HP won't search for enemy, but stay in "Flee" mode.
 
Last edited:
@Justin I watched the video with the integration of the character controller and it was really helpful, even though I don't like much the approach of setting data in the tree (hehe :p). Now I have things more clear in my mind. As it was an integration tutorial, I never thought it could be useful for BD itself, but it was!

@abc123 I also tried your approach and it works nice, and I came up with a mix between what I had and your approach, so, here it is my 2.0 version of the full tree. I will put here too a short description of what my own nodes are doing (I am not using any package or integration with A* project directly in the tree, but more in the upper levels of the logic of my entities).

Tree.PNG

Flee.PNG

· Is Any Hostile Visible: returns success if hostile visible, failure if not
· Is Low HP: returns success if HP is below a certain threshold, failure if not
· Knows Last Hostile Visible Position: returns success if the AI knows the last visible position of the last visible hostile, failure if not
· Look At Hostile Target: just calls once to the LookAt, and then stays running until some external abort ends it. When it's ended, I stop the lookAt.
· Shoot: a shooting routine, always running, must be aborted from outside. It stops on end
· Follow Hostile Target: same as shooting, but following instead
· Go To Last Hostile Visible Position: goes to that position, and returns success on arrival
· Reset Last Hostile Visible Position: well, resets that position
· Flee/Wander/Patrol/Defend: just movement routines that are always running, aborts handle it's termination

This is working nice doing the behaviour that I explained before, but I am pretty sure that it can be improved and it still has errors. My intention is to have the most atomic branches (in external BTs) as possible, to reuse and mix them, and create variations of this full behaviour tree by just dragging and dropping external BTs, but I am having trouble on "atomicing" them. Also, I don't like having the Flee twice, I would like to have it in the most left branch just once (and repeat the attack nodes, if necessary, in other branches), but I couldn't figure out how to use it together with the Is Any Hostile Visible conditional.

And just for finishing, a couple of dumb doubts:
- What is more expensive, having the LookAt running with an empty update (just returning Running all the time) or having a node that stops the look at, located in the right of the branch that has the look at?
- I don't need to return success in the update if it has no logic and I just want an immediate success, right?
- For conditional checks, is there any way to use delegates instead of checking the update every tick? That would be very convenient, as my project is very event driven.


Thanks for all the efforts trying to help me, I really apreciate it!
 
This is working nice doing the behaviour that I explained before, but I am pretty sure that it can be improved and it still has errors. My intention is to have the most atomic branches (in external BTs) as possible, to reuse and mix them, and create variations of this full behaviour tree by just dragging and dropping external BTs, but I am having trouble on "atomicing" them. Also, I don't like having the Flee twice, I would like to have it in the most left branch just once (and repeat the attack nodes, if necessary, in other branches), but I couldn't figure out how to use it together with the Is Any Hostile Visible conditional.
You shouldn't have full Flee behavior reference inside the Search branch, this is logically wrong. There must be just a single IsLowHP conditional task inverted. If you don't want to repeat Flee in different branches, but make it a separate branch on the left, then you run into another problem: you have to split the new Flee branch into combat and non-combat cases, passing Enemy variable, duplicating Aim and Shoot tasks. Decide for yourself what's better)

I have similar situation in my tree, AI agent may want to pick up an item or run into a healing zone, if either is detected nearby. This can happen both in and out of combat, so outlined tasks are duplicated in corresponding branches. I don't know how to solve this problem in another way, maybe some clever use of parallels?
111.PNG
It doesn't bother me much, though. Moreover, conditionals on these tasks are set up slightly differently. For example, the detection range for some items is lower during combat than during patrol.
 
Last edited:
You shouldn't have full Flee behavior reference inside the Search branch, this is logically wrong. There must be just a single IsLowHP conditional task inverted. If you don't want to repeat Flee in different branches, but make it a separate branch on the left, then you run into another problem: you have to split the new Flee branch into combat and non-combat cases, passing Enemy variable, duplicating Aim and Shoot tasks. Decide for yourself what's better)

If I remove the full flee, then flee only would happen if I am watching an enemy, because of the most left conditional in the tree. Somehow I need two full references of, either attack, or flee, but I agree with you that maybe a better use of parallel could help avoiding duplicated nodes. Maybe @Justin could guide us a bit on this direction?

Also if @Justin (or anyone that could actually answer xD), if you could help me with this small doubts that I posted before, would be very thankful :D

- What is more expensive, having the LookAt running with an empty update (just returning Running all the time) or having a node that stops the look at, located in the right of the branch that has the look at?
- I don't need to return success in the update if it has no logic and I just want an immediate success, right?
- For conditional checks, is there any way to use delegates instead of checking the update every tick? That would be very convenient, as my project is very event driven.
 
Last edited:
- What is more expensive, having the LookAt running with an empty update (just returning Running all the time) or having a node that stops the look at, located in the right of the branch that has the look at?
- I don't need to return success in the update if it has no logic and I just want an immediate success, right?
- For conditional checks, is there any way to use delegates instead of checking the update every tick? That would be very convenient, as my project is very event driven.
- In both of those cases I don't think you are going to notice any bottleneck from it. In general though the less work the tree has to do (such as traversing different nodes) the better but for that situation you'll have no problem.
- OnUpdate always requires a return parameter, but that can be TaskStatus.Success.
- You could tick the Behavior Manager manually from your system: https://opsive.com/support/documentation/behavior-designer/behavior-manager/. This will then only update the tree when you tell it to.
 
- In both of those cases I don't think you are going to notice any bottleneck from it. In general though the less work the tree has to do (such as traversing different nodes) the better but for that situation you'll have no problem.
- OnUpdate always requires a return parameter, but that can be TaskStatus.Success.
- You could tick the Behavior Manager manually from your system: https://opsive.com/support/documentation/behavior-designer/behavior-manager/. This will then only update the tree when you tell it to.
For the first and last answers, got it :D. For the second one, I mean if I don't override the OnUpdate, it automatically returns success, and it has no issues, right?

Thanks!
 
That's correct.
Awesome, all clear then!

@abc123 Now I have a much better tree, and my next thing to add to it is the check to know if the Fire line is free of friendly entities, so I don't destroy them. I did this, but I am not sure that this is the best way to do it... What's your opinion guys? (the picture is just the external bt with the combat logic). I don't like the repeater, but I can't find another way to reevaluate only that branch of the parallel. Any improvements to this that could pop up in your mind?

CombatNoFF.PNG
 
Awesome, all clear then!

@abc123 Now I have a much better tree, and my next thing to add to it is the check to know if the Fire line is free of friendly entities, so I don't destroy them. I did this, but I am not sure that this is the best way to do it... What's your opinion guys? (the picture is just the external bt with the combat logic). I don't like the repeater, but I can't find another way to reevaluate only that branch of the parallel. Any improvements to this that could pop up in your mind?

View attachment 3784

It's tough to say, not being in context of your project. But anyway, before the most expensive checks, I would add all sorts of other conditions first:
- is shooting possible? (previous animation finished playing)
- is ammo enough? (otherwise can't shoot, need to reload)
- is enemy in range of weapon? (square distance check)
- etc

By the way, you don't need "return success" decorator under repeater. To achieve the same effect, select "repeat forever" and "don't end on failure" in repeater options.
 
Last edited:
It's tough to say, not being in context of your project. But anyway, before the most expensive checks, I would add all sorts of other conditions first:
- is shooting possible? (previous animation finished playing)
- is ammo enough? (otherwise can't shoot, need to reload)
- is enemy in range of weapon? (square distance check)
- etc

By the way, you don't need "return success" decorator under repeater. To achieve the same effect, select "repeat forever" and "don't end on failure" in repeater options.
Heyy, sorry for the late response, I was on holidays :D. The animations are handled outside, that's not a problem. About ammo, that's handle inside the shooting node. The weapon range will be handled outside too, nothing to worry about.

About the return success, you are right, but wouldn't that mean that the parallel would fail, and then it would start all over because the IsAnyHostileVisible condition would still be true? This way maybe I am helping the tree to not restart everytime I have the fire line blocked, and as @Justin says, less tree travels are better, don't you think? The repeat for ever will be active anyway, that's true tho.

Thanks!
 
Top