[Request/Enhancement] Conditional aborts and remembering node position.
Opened this issue · 3 comments
Coming from Unity using Behavior Designer I miss the concept of conditional aborts, I also noticed that in hxbt the tree is completely reevaluated. Before I start making changes myself I'm also interested in your input on this.
It is often handy in behavior trees to return to a previous point if certain conditions have changed. Behavior Designer and UE4 have so called conditional aborts ( in UE they are called observer aborts, not a fan of the name). This does not only allow you to simplify your trees it keeps the flow of logic clear.
Conditional tasks/behaviors are no different than other tasks returning a success or a failure, however they are reevaluated periodically to check if their conditions have met. They are only reevaluated if and only if the task/parent is on the right side of the current tick and if a abort type is set on the composite parent.
This means that the condition tasks need to be stored somewhere ( most likely in the tree object) and updated periodically by a default value or the value set by user.
Composite nodes are the only nodes that can have a abort types which are
- None -> No abort type
- Self -> can only abort a task if both the conditional and the current task have the same composite parent node.
- Lower priority -> can abort any branch/task that came after the current one.
- Both -> adhere to Self and Lower
These types can be enum values that you can set in composites.
The conditional class could look as simple as
class Conditional<T> extends Behavior<T>{}
And then Conditional is extended to implement your own logic.
When a conditional is added to a composite there needs to be a way to reevaluate them. My first thought is to add the conditional to the behavior tree once you enter the composite node. Each composite should know which tasks are conditional task ( perhaps by doing a simple Std.is when adding them to a composite) so it's easier to handle them. Perhaps the composite should be added to the behavior tree instead and then check its conditionals trough a function.
However considering the self
abort type, reevaluating its conditionals should stop once the current task is no longer part of the same parent. Which isn't the case for Lower priority
Once an abort is triggered there needs to be a way to tell the behavior tree to switch out of the current branch and relocate it's current position to the composite holding the conditional task and continue from there on out.
Another thing ( and i might be wrong on this ):
I've noticed that every tick starts from the root. To me it sounds more logical that when a task returns RUNNING
the current node ( or its composite ) is referenced in the behavior tree so it can continue where it left off without starting from the root. This is also where conditional aborts play a roll so that you can break out of a RUNNING
task.
Any thoughts on this?
This is very interesting. I must admit I haven't worked with the behavior tree in Unreal Engine and will have to look a bit more at this. I've planned for a while now to add support for resuming where the tree left off but one of the big concern with this, that I haven't had the time to solve yet is that a behavior tree needs to be reusable by different agents. A possible solution to this would be to store current node in the blackboard.
So in short, if you have 100 agents with the same behavior, they should be able to reuse the same tree, and not create their own instance of the tree.
I like your ideas and input, and will definitely look into not reevaluating the tree every frame. I must admit that I'm not very familiar with the concepts of abort types though, never actually heard of these before in the context of behavior trees, but they sound very interesting. Conditonals in conjuction with node position is definitely a welcome addition to the library, I'll have to take a closer look at aborts before I make up my mind about those though.
I'm not sure when I'll have the time to implement these myself, as I'm currently working full time as a game developer, so if you feel that this is something you're in bad need of, before I have the time to implement it myself, a pull request is very welcome.
So far I'm just playing with it, nothing serious.
I haven't really considered the fact multiple agents could use the same instanced tree. But I can imagine that always starting from the root means it has to check certain nodes again. Some of these nodes could contain computational heavy tasks. And checking them every period seems like a waste. But yes you'd lose the ability to apply the same tree if a tree contained agent specific states.
Coming from Behavior Designer using Unity I haven't really worked with blackboards, at least not in the context how this lib or unreal4 does it. So I'm not too sure how they should be handled. Is the idea that every agent have its own blackboard instance? If so then it makes sense to add agent state ( data about the tree, such as position, etc ) to the blackboard.
I could be wrong but it looks like Unreal only allows abort types on Decorators. I haven't been able to do it on Composites. In Behavior Designer abort types are set on Composites. So it seems like there are different choices on how to implement it. Don't know the reasons behind either of them though.
I don't have a solid solution myself yet so im still tinkering with my thoughts. I'm still figuring out on how to properly make use of blackboards in my own stuff as well.
Thanks for your feedback on this! I'm gonna try to get time to download UE4 during the weekend and play around with their behavior tree stuff just to see what theirs is like.
Yes one of the big advantages of behavior trees is minimizing memory footprint by reusing the same tree. The blackbord is there to contain an agents context of the world, and all of their sensors, and behaviors are not meant to hold any member data, only local variables.
To store node position and not reevaluate the tree each frame I have to decide which states that trigger a reevaluation, I don't have a clear solution to this yet, but I'm going to see if I can work something out regarding that during the weekend as well.
The behavior tree that UE4 uses seems to be a custom implementation of behavior trees, this library is closer to text book examples of how they work.