How to save and load behavior trees?

caleidon

Member
As I've posted in the past (post that got 325 views), I'd like to request a save / loading system.

I know that this is a planned feature for version 2, but it's been nearly 3 months since that post and there hasn't been any update since. I believe this is a crucial feature because almost any AI-based game NEEDS to save the state of that agent. Mine certainly does and this is tipping point for me as I cannot proceed without it.

Has there been any progress on a saving / loading system and is there an estimate of when we can expect this update?
 

Justin

Administrator
Staff member
I am merging that thread with this one so it's easier to follow.

Due to the number of moving parts I am unable to provide an ETA for new features. I will let you know though when it is close to being released. If it's something that you need now I would not wait on my implementation. As I mentioned in that other thread, I have heard others successfully implementing this so it is definitely possible.
 
@caleidon I save the shared variables of interest and load them before enabling my behaviour tree. My tree always starts from the beginning but that is not an issue I think if you have the saved variable values that should guide your tree to where it ended last. Its not perfect but until there is an easy way to load the exact node that was active when saving, it will work.

For the future I hope each node gets an OnLoad and OnSave method we can override, then we we save/load our game we can just call behaviourTree.Save(), for example, which would run OnSave in the current node and save the current node id.
 

caleidon

Member
I am merging that thread with this one so it's easier to follow.

Due to the number of moving parts I am unable to provide an ETA for new features. I will let you know though when it is close to being released. If it's something that you need now I would not wait on my implementation. As I mentioned in that other thread, I have heard others successfully implementing this so it is definitely possible.
@Justin Based on what Max said, doing that plus saving all the variables might be a good way to make a makeshift save load system. Is there no easy way to get and save the exact node that was active when saving?
 
Last edited:

caleidon

Member
@Justin Any chance on an answer? I really need to start doing save/load stuff. Is there any other helpful tips you might have? Like where to find the node that was last active in a tree and how to resume a tree from that point?
 

Justin

Administrator
Staff member
All of the active behavior tree states is saved in the BehaviorManager.BehaviorTree class. It is going to take some finessing in order to get it resuming correctly but if you save everything within the BehaviorManager and all of the task values then you should get a match.
 

caleidon

Member
I'm going with the route of just serializing literally everything and I think this might function (with a lot of work). When loading my tree when testing, I just assign the BehaviorManager.instance.BehaviorTrees[0] = loadedTree and that works. However, I'd now like to attach this loaded tree to a character inside of my game.

Characters have the BehaviorTree component on their gameobject and I can't figure out how to assign the loaded tree (which is of type BehaviorManager.BehaviorTree) to it. The component also has a property of ExternalBehavior but I can't assign the loaded tree to that either.

Any sort of advice on how I could transform and attached the loaded tree to my character's gameobject would be extremely helpful as I am quite stuck right now and don't understand how the source code works.

Thanks Justin. :)
 

Justin

Administrator
Staff member
The BehaviorManager does not have a setter for the internal BehaviorTree object so you'll need to import the source version to add it. When you import the source make sure you first remove the Asset Store's Behavior Designer folder.
 

caleidon

Member
I've already done all of that, but my question is how do I, concretely, assign a BehaviorManager.BehaviorTree to a gameobject?

Screenshot_2.png

It feels like the type "BehaviorManager.BehaviorTree" (which is what I save and load) is so disconnected from all other parts of Behavior Designer and I have no idea on how to "plug it into a gameobject" after I've loaded it.

So, if I was going to add a setter, where do I need to add the setter and where should I assign this tree that I just loaded?
 

Justin

Administrator
Staff member
You can't. The BehaviorTree object is not a MonoBehaviour.

In your screenshot you have a BehaviorTtree object but are assigning it to the ExternalBehavior property. Those are two different objects so you cannot assign it. You will need to create a new function within the BehaviorManager and add it to the behavior tree list there.
 

caleidon

Member
You can't. The BehaviorTree object is not a MonoBehaviour.

In your screenshot you have a BehaviorTtree object but are assigning it to the ExternalBehavior property. Those are two different objects so you cannot assign it. You will need to create a new function within the BehaviorManager and add it to the behavior tree list there.
That seems simple enough, but when I add the tree to the behavior tree list within BehaviorManager, how will the game know that this tree is attached to my Character1 and control its gameobject, as opposed to Character2's gameobject. That's the part I'm confused about.

In other words, when I normally add a BehaviorTree component to my Character's gameobject, where is the connection between the BehaviorManager.BehaviorTree that I save/load (which is saved in BehaviorManager.instance.BehaviorTrees) and the character's gameobject? Or am I misunderstanding something, as in, does it make a difference if a tree for each character would be sitting on one gameobject, as opposed to each tree sitting on its respective owner?
 
Last edited:

Justin

Administrator
Staff member
Have you imported the runtime source code? You'll need this to make modifications and to get a better understanding of the internal functions.

The BehaviorManager.behaviorTreeMap dictionary will make a Behavior MonoBehaviour (the component on the GameObject) to the internal BehaviorTree object. This mapping is used when you want to enable/disable a specific Behavior component on a GameObject.
 

caleidon

Member
Have you imported the runtime source code? You'll need this to make modifications and to get a better understanding of the internal functions.

The BehaviorManager.behaviorTreeMap dictionary will make a Behavior MonoBehaviour (the component on the GameObject) to the internal BehaviorTree object. This mapping is used when you want to enable/disable a specific Behavior component on a GameObject.
That's exactly what I was looking for and had no idea that it existed! Thank you! So all I have to do when I load a tree is to add it to BehaviorManager.instance.BehaviorTrees (which is where all the trees are kept internally I assume) and then also connect that loaded tree to my Character by adding a new entry where I connect his Behavior (which is the component on his Gameobject) to my newly loaded tree (which is BehaviorTree in the dictionary)?

I'll give this a shot!
 

caleidon

Member
Have you imported the runtime source code? You'll need this to make modifications and to get a better understanding of the internal functions.

The BehaviorManager.behaviorTreeMap dictionary will make a Behavior MonoBehaviour (the component on the GameObject) to the internal BehaviorTree object. This mapping is used when you want to enable/disable a specific Behavior component on a GameObject.
I've tried doing what you've suggested but saving the entire tree for only one character (and it's a pretty simple tree) is 3MB when everything gets saved.

I'd like to try a different approach where I don't save the entire tree, but load an existing one and just save all the required states and variables that I need. Is there some way to save the node that a particular tree was running while saving and then when loading force the tree to resume from that node as usual?
 
I've tried doing what you've suggested but saving the entire tree for only one character (and it's a pretty simple tree) is 3MB when everything gets saved.

I'd like to try a different approach where I don't save the entire tree, but load an existing one and just save all the required states and variables that I need. Is there some way to save the node that a particular tree was running while saving and then when loading force the tree to resume from that node as usual?
Id also be interested in knowing this, really wish there was some API documentation so I didnt have to come here for this stuff :)
 

Justin

Administrator
Staff member
I'm glad that you were able to get at least something working.

I'd like to try a different approach where I don't save the entire tree, but load an existing one and just save all the required states and variables that I need. Is there some way to save the node that a particular tree was running while saving and then when loading force the tree to resume from that node as usual?
There isn't anything like that built in. It definitely sounds possible but will take some new code within the Behavior Manager.
 

caleidon

Member
I'm glad that you were able to get at least something working.


There isn't anything like that built in. It definitely sounds possible but will take some new code within the Behavior Manager.
Since you said you couldn't provide even a rough ETA for save/load system (which I'd love to hear), could you maybe in the near future make a small update to Behavior Designer where you could implement that functionality? There wouldn't be any saving, loading or serialization needed, just provide the API so we can get the currently running node within a tree and the API so we can force that node to resume running?

It would at least give us an option to implement a reasonably easy workaround for saving/loading while we wait for your official implementation. I know it's a lot to ask for, but for my game where AI and agents are in the center of gameplay this is a crucial feature and I believe it should be of high priority. Thank you.
 

Justin

Administrator
Staff member
More than one node runs at a time so all of the active nodes are saved in the List<Stack<int>> activeStack data structure. By saving off and restoring the BehaviorTree object you should also be doing this. So you want to just replace this single list? By doing this without saving out the rest of the variables you'll end up in an error state.
 

caleidon

Member
More than one node runs at a time so all of the active nodes are saved in the List<Stack<int>> activeStack data structure. By saving off and restoring the BehaviorTree object you should also be doing this. So you want to just replace this single list? By doing this without saving out the rest of the variables you'll end up in an error state.
Would it be enough to ONLY save the activeStack data structure and assign it to a tree when loading, or is there any other such variables that absolutely NEED to be saved? Of course, aside from any shared variables that need to be loaded for the tree to resume as it left off.
 

Justin

Administrator
Staff member
No, you will need to save all of the values within the BehaviorTree object. They each are necessary in order for you to fully restore the tree.
 
Top