I’ve found cases where it looks like a BTTask (and even a BTService) may have a frame or so of latency in terms of continuing execution after a decorator condition fails in its node.
Originally my BTTask was written to operate under the strict assumption that it only ran if and while its decorator constraints were met but I found out after some extensive testing (hunting for a particular issue) that sometimes the task ran “one more time” after one of the decorator constraints failed (it appears that there may be some latency in switching trees?).
This may be as designed I’m not sure but I am posting it here because it ends up bungling some of the nice data-encapsulation: I essentially have to guard any changes to Blackboard state made by my task with a set of equivalent checks for Blackboard state that my node decorators already do so there are two sets of condition checks to manage.
This is by design. Handling aborting and starting tasks is done solely in BehaviorTreeComponent's tick. Anything that happens outside of it is stored for processing on following tick. The reason behind this is that it’s possible to have multiple aborting requests happening in a single frame, and if we were to look for next task to perform we could end up spending a lot of time doing unnecessary work.
I suspected this kind of latency was by design. Do you have any advice on how to make it cleaner on my end: is there a function that can be exposed to a Task to check the containing nodes conditions?
I don’t know how exactly your task breaks with decorator-checked conditions no longer true, so I’ll make a wide guess.
Firs of all, sanity check your data. You can’t depend on decorators to guarantee data correctness for your task since you can’t force given decorator to be used when your task is used.
If your task works regardless of data, but produces behavior-breaking actions, maybe you should consider a latent mechanism for triggering those actions?
Conceptually AI should be build with a kind of momentum in mind, so that a, say, 0.2 second slip in decision making or action performing won’t make a visible difference. I can just say from my experience that this kind of approach has many advantages like reducing decision osculations or allowing easier calculation or execution batching.
You are correct it does indicate an issue with design. I had assumed the decorators were contracts. In my game units are partially autonomous, you can give a unit an order to move to a position, guard, or attack.
After executing those player initiated commands they resume an autonomous mode. The particular issue occurring was the autonomous BT branch was occasionally overwriting the target key (because the actor to target was a BB key shared by the player command branch and the autonomous branch).
This led to odd behavior on occasion where the player would order a unit to attack and the unit would either do nothing or attacking the wrong thing (since the autonmous branch executed again and overwrote the target key). This can easily be fixed by not having those branches share a BB key, but I think this kind of behavior needs to be strongly advertised.
The way the BT documentation is written Decorators sound like a contract: Nodes won’t execute if Decorators tests fail. In reality it’s more latent than that, and if people aren’t aware of that it can lead to hard-to-find issues.
Thanks again guys for all your work on UE4. It’s an incredibly well built piece of technology.