Changing External Behavior Tree at Runtime

Hi,

We would like to change the external behavior tree at run time. Like to know how and which way is efficient. Our game is like a city full of AI[say more than 200], we have AI pool and behavior pool. We get AI from the pool, add initial data, add/change behavior/external tree from the pool then activating the AI in the game.

1.jpg
We have a base tree for all Guest AI's "GuestBaseBehaviorTree", which has an external behavior tree reference task called "CurrentBehavior". Initially, we thought, we can directly change this at runtime[Marked in green], but after going through a couple of threads in the forum,

Forum thread:
https://opsive.com/forum/index.php?...havior-at-runtime-not-working.1597/#post-7716

and as well as in discord. It seems like we need to start a fresh tree for every different functionality/behavior tree we want to change in the runtime.

Also, we bought and checked Movementpack and Formationpack demos too. It explains, disabling and enabling the behaviors at runtime. But we thought we could create a pool of external behavior trees and update it in behavior tree reference task in runtime.

Questions:
1. As you have mentioned in those threads, we need to make a copy of the entire set up, means, do we need to have different GuestBaseBehaviorTrees, such as "Wander[Marked in Red]",
3.jpg
"SearchForBusiness[Marked in Yellow],
4.jpg

So If we understood correctly, we basically need switching this entire set up at runtime and means, changing External behavior in the BehaviorTreeComponent at run time, right?

2. And is this the only way to switch behaviors or any other way around?

Thanks in advance,

Out of topic: Thanks for creating this awesome asset.
 
Last edited:

Justin

Administrator
Staff member
Here's also a post that should help: https://opsive.com/forum/index.php?threads/external-tree-runtime.2261/

So If we understood correctly, we basically need switching this entire set up at runtime and means, changing External behavior in the BehaviorTreeComponent at run time, right?
If you want to be able to change the Behavior Tree Reference multiple times at runtime you will need to make your existing tree an External Tree and then load that external tree. When you want to swap out the functionality you can then reload the original external tree and BehaviorTreeReference.GetExternalTree will be called again.

2. And is this the only way to switch behaviors or any other way around?
Depending on what you are doing you may not need external trees at all and could just load everything up front and use a conditional task to determine if that branch should run.

Out of topic: Thanks for creating this awesome asset.
:)
 
C#:
BaseTree.DisableBehavior();
_newExternalBaseTree = Instantiate(BaseExternalTree);
_newExternalBaseTree.Init();
BaseTree.ExternalBehavior = _newExternalBaseTree;

_newBehaviorTree = ObjectPoolHandler.GetCharacterBehaviorFromPool(targetBehaviorType);
Debug.Log("New behavior : " + _newBehaviorTree.name);

if (_newBehaviorTree != null)
{
    BaseTree.FindTask<BehaviorTreeReference>().GetExternalBehaviors()[0] = _newBehaviorTree;
}
else
    Debug.Log("Failed to create new behavior tree");

BaseTree.EnableBehavior();

Okay, we have created few external trees, such as "BaseExternalTree", "Initialization" and "Wander". Whenever we need to change from one behavior[Initialization] to another[Wander], tested on keypress, we are instantiating the BaseExternalTree, so it creates a fresh copy with a new empty BehaviorTreeReference task, then we get the [_newBehaviorTree]"Wander" external behavior tree from the pool and assign in its array as its first element. This works fine.

Got a couple of questions though,

1. Will it be an issue every time we create/instantiate a new base tree[BaseExternalTree], what happens to the previous one when we replace it with the new tree? like memory issues? Or BD handles it?

2. And As you have seen, we have a task called "UpdateStatsOverTime", we are updating set of global variables in this task, these variables are also getting reset every time we create a new tree, so we are guessing, we need to have a separate script out of BT and keep it running always and reference them back again in BT tasks? Or any other idea?

3. Also reload means, instantiating[we assumed this and tried the above experiment] the tree freshly or did you mean something else?

Thanks,
 
Last edited:

Justin

Administrator
Staff member
1. Will it be an issue every time we create/instantiate a new base tree[BaseExternalTree], what happens to the previous one when we replace it with the new tree? like memory issues? Or BD handles it?
You'll want to profile but whenever the tree reloads it will have to cache the new data structures. Obviously you'll want to limit the amount of time that you completely reload the tree.

2. And As you have seen, we have a task called "UpdateStatsOverTime", we are updating set of global variables in this task, these variables are also getting reset every time we create a new tree, so we are guessing, we need to have a separate script out of BT and keep it running always and reference them back again in BT tasks? Or any other idea?
If UpdateStatsOverTime uses a global variable within the external tree it should keep that reference when it reloads.

3. Also reload means, instantiating[we assumed this and tried the above experiment] the tree freshly or did you mean something else?
You could use a pool so you don't need to instantiate every time. When the behavior tree loads it will make a copy of the external tasks so it won't directly use the pooled objects.
 
Last edited:
Currently, on a keypress in a single frame with 100 AIs in the scene, we are creating the base external tree and then getting new behavior[wander] from the pool and assign it in its external tree array. Not sure this is okay or not for this experiment. Please share your ideas. Thanks.



PoolTree.jpgProfilerOp.jpg
 

Justin

Administrator
Staff member
Instead of doing them all at once you should spread out the reload over many frames. When you reload the behavior tree it has to create new instances of the data structures and this can get heavy if you do a lot at a single time. Also make sure you are pooling the original external behavior tree.
 
Hi,

Sorry for the late reply.

We doubt, still there can a good chance for these kinds of scenarios. Such as more than 100 guests changing their behavior at a time. Imagine its like a "RollerCoaster Tycoon" kind of game, can be filled with 200 or more guests in the game. Each guest can make a decision based on some attributes and then change their behavior[external tree] from one to another in runtime.

And yea, all guest objects and trees[including original external behavior tree] are instantiated and initialized[serialized] and pooled.

Our current plans are,

1. Keeping all the tasks in a single tree with few external tree reference tasks in the original base tree itself, no external tree switching in runtime at all. But we doubt this also becomes hard to manage/debug at the later stage of this game as we keep adding more tasks/external tree reference tasks.

2. Keeping different behavior tree components for each behavior[as explained in movement/formation pack scenes]. Enable/disable as needed at runtime. But we need to add new behavior tree components whenever we need to add new behavior to the guest character.

3. Assume this kind of scenario hardly possible in the game. Go with this external tree switching, for now, worry about performance when it actually happens in the game.

Question,

Just to make sure, what do you mean by "spread out the reload over many frames"?. Any specific technique in the case of BD?.

Any other expert suggestions from your side will be really helpful.

Thanks in advance.
 

Justin

Administrator
Staff member
It does sound like you have the correct approach for the different options. I am currently working on version 2 and would like to make this scenario easier - if you have any workflow suggestions I'd be happy to hear it.

Just to make sure, what do you mean by "spread out the reload over many frames"?. Any specific technique in the case of BD?.
Not specific to BD - it could be as simple as starting a coroutine and doing a max of n swaps during a single update.
 
It does sound like you have the correct approach for the different options

I guess we were not clear about the current plans/approaches mentioned in our previous post.

We wanted to ask you, which one would you suggest, among those 3 current plans/approaches for our kind of game?.

I am currently working on version 2 and would like to make this scenario easier - if you have any workflow suggestions I'd be happy to hear it.

Thanks for asking, we have been following your updates on discord too, seems like you are already covering everything needed. Hopefully expecting the version 2 soon :)

Not specific to BD - it could be as simple as starting a coroutine and doing a max of n swaps during a single update.

Okay thanks, and yea makes sense. We will end up having more of these kinds of scenarios[not only BD update] in the future. We will need to balance somehow, will think more about this too.

Thanks.
 

Justin

Administrator
Staff member
We wanted to ask you, which one would you suggest, among those 3 current plans/approaches for our kind of game?.
Unfortunately there isn't a 'right' answer for this. Personally I try to keep everything within a single tree so you can see how everything relates and then use external trees for repeated functionality. This has the advantage in that you can choose to separate things out, but when the tree loads you won't need to instantiate any new trees.
 
Personally I try to keep everything within a single tree so you can see how everything relates and then use external trees for repeated functionality. This has the advantage in that you can choose to separate things out, but when the tree loads you won't need to instantiate any new trees.

Okay, this will help us get into a direction.

Thanks a lot.
 
Top