Best or correct way to reload BehaviourTreeReference task?

bbjones

Member
Continuing on from this thread

My custom BehaviourTreeReference task returns whichever job is assigned to the worker at runtime, which changes throughout the game, this seems to be working correctly.

But, I can't figure out how to get the BehaviourTreeReference task to re-evaluate, it remains stuck with the first BT it returned when the scene/agent loaded the first time.

I've tried re-assigning the BT to the agent, disable/enable the BT, but no luck.

The only way I can get it to reload so far is to assign an entirely different ExternalBehaviourTree, then assign my common external tree back, that will then trigger the BT reference to load the right job tree.

I have a breakpoint in my custom class and it only ever gets hit once.

C#:
    public class LG_SetAiJobTree : BehaviorReference
    {
        public override ExternalBehavior[] GetExternalBehaviors()
        {
            List<ExternalBehavior> btList = new List<ExternalBehavior>();
            btList.Add(LGAI_Manager.Instance.GetCurrentJobTree(this.gameObject.GetComponent<LGAI_Simple>()));

            this.externalBehaviors = btList.ToArray();

            return base.GetExternalBehaviors();
        }
    }
 
Last edited:
GetExternalBehaviors is only called when the behavior tree is initially loaded. Any subsequent enables/disables will cause the current tree to reload.

In order to have GetExternalBehaviors called again you will want to save off your behavior tree it its own external tree, and then reassign the BehaviorTree.ExternalBehavior property to the external tree. This will trigger a complete reload and call GetExternalBehaviors again.
 
...reassign the BehaviorTree.ExternalBehavior property to the external tree. This will trigger a complete reload and call GetExternalBehaviors again.

That was my initial design but that doesn't seem to reload the BT when I assign the same BT the second time (or any additional time after that).
It still runs the BT fine but in the original state with the original tasks, GetExternalBehaviours isn't running more than once.

For example, when I first load my agents I call this init method which assigns a BT from my AI manager. That BT is the one with the BehaviourTreeReference task that returns the current AI job sub-tree.
C#:
        public void InitSimpleAI(LGAI_Simple simpleAI)
        {
            simpleAI.behaviorTree.ExternalBehavior = this.SimpleAI_DefaultBehaviour;

            simpleAI.SetDefaultSpeeds();
        }

During gameplay when I assign my workers new jobs I call that Init method again but nothing changes. The BT doesn't even seem to reload, it's like it ignores the change because the BT is the same.
I also put a button on my AI component to call that Init method on demand, again nothing happens.

So far the only way I can get it to consistently work is with manual intervention:
  • Run the game, the agent loads their default job tree via the init() method above as expected
  • I then change jobs on the agent
  • Then I manually swap out the default BT in my AI manager so the init() method loads a different external tree to the agent
  • Then I manually swap back to the job BT with the reference task and call init() again and only now does GetExternalBehaviours get called the second time loading the correct sub-tree for the new agent's job
In another test I have a button on the agent where it will call the init() method on demand at runtime.
  • Run the game, agent loads their default job as usual
  • Call init() via the debug button and GetExternalBehaviours is not called again
To sum it up, calling init() does not appear to cause the tree to reload or even restart. That only happens if I call init() and the external tree being assigned is different from the current external tree.
 
And in looking at the ExternalBehaviourTree property it looks like it will just return without taking any actions if the incoming BT is the same as the current BT, which matches up with what I'm seeing:


C#:
        public ExternalBehavior ExternalBehavior
        {
            get
            {
                return externalBehavior;
            }
            set
            {
                if (externalBehavior == value)
                {
                    return;
                }
                ...

Changing my init() method to null out the external tree before re-assigning the same BT seems to work for now.
C#:
        public void InitSimpleAI(LGAI_Simple simpleAI)
        {
            // for now, set null first so it will recoginize the change
            simpleAI.behaviorTree.ExternalBehavior = null;
            simpleAI.behaviorTree.ExternalBehavior = this.SimpleAI_DefaultBehaviour;

            simpleAI.SetDefaultSpeeds();
        }
 
Last edited:
While that code above works, it does generate a runtime error everytime I call it:
The behavior "Behavior" on GameObject "__MyAgentGameObject" contains no root task. This behavior will be disabled.
 
Are you sure that you're loading a tree that contains a root task? I just tried to repro that error but wasn't able to. Here's my test assets. If you can reproduce that error from this package I should have a better idea of the problem.
 

Attachments

  • Repro.unitypackage
    4.1 KB · Views: 3
Your repro works as expected.

I don't see what is different with mine beyond mine of course has larger and more complex trees.

And in mine I can bypass the error by wrapping things in Disable/EnableBehaviour as you did, but I also must include enable/disable otherwise my trees end up non-functional. No errors and everything is enabled but the trees simply don't run, and there is no green progress when viewing the BT in the editor at runtime, so not sure what else is going on.

But for now this works, no errors, so I'm running with it :)

C#:
            simpleAI.behaviorTree.DisableBehavior();
            simpleAI.behaviorTree.enabled = false;
            simpleAI.behaviorTree.ExternalBehavior = null;
            simpleAI.behaviorTree.ExternalBehavior = this.SimpleAI_DefaultBehaviour;

            simpleAI.behaviorTree.EnableBehavior();
            simpleAI.behaviorTree.enabled = true;
 
Top