CanExecute running twice

caleidon

Member
In my behavior tree, I have a custom composite task called JobChooser.

Just for the sake of it, here is the code:

C#:
using BehaviorDesigner.Runtime.Tasks;
using UnityEngine;

public class JobChooser : Composite
{
    // The index of the child that is currently running or is about to run.
    private int currentJobIndex;

    // The task status of the last child ran.
    private TaskStatus executionStatus = TaskStatus.Inactive;

    private Human human;

    public IJobInfo currentJob;

    public override void OnAwake()
    {
        human = GetComponent<Human>();
    }

    public override int CurrentChildIndex()
    {
        Debug.Log($"Getting currentjobindex");
        return currentJobIndex;
    }

    public override bool CanExecute()
    {
        if (!JobManager.IsJobAvailable(human))
        {
            Debug.Log($"Job isn't available, wandering");
            currentJobIndex = (int) JobType.Wander;
            return true;
        }

        // TODO RENAME JOBTYPE TO JOBINDEX
        currentJob = JobManager.RequestJob(human);
        currentJobIndex = (int) currentJob.JobType;

        return true;

        // if (Children[currentJobIndex].FriendlyName != currentJob.JobType.ToString())
        // {
        //     JobManager.EnqueueJob(currentJob);
        //     throw new Exception("Job order in BT incongruent with JobManager Job enum!");
        // }
    }

    public override void OnChildExecuted(TaskStatus childStatus)
    {
        // JOB IS COMPLETED HERE - CHECK CANEXECUTE AND KEEP LOOKING FOR JOBS
        executionStatus = childStatus;
        Debug.Log("CHanged task status");
    }

    public override void OnConditionalAbort(int childIndex)
    {
        // The job chooser was interrupted.
        currentJobIndex = 0;
        executionStatus = TaskStatus.Inactive;
        Debug.Log("JOB CHOOSER WAS ABORTED CONDITIONALLY()");
    }

    public override void OnEnd()
    {
        // OnEnd() is called when there are no available jobs for this creature.

        executionStatus = TaskStatus.Inactive;
        Debug.Log($"JOB CHOOSER CALLED ONEND()");
    }
}

There is nothing unusual about it.

The problem is that the CanExecute override consistently runs twice every behavior tree tick. I've tried letting the Behavior Manager do the ticks automatically, I've even tried to manually tick it once every 5 seconds. No matter what I do, CanExecute will always run twice in every tick, causing unwanted behavior (requesting a job for my character twice, instead of once).

I'm not sure why this happens - my understanding is that when CanExecute returns true or false, the Task should check what the currentJobIndex is and run the child task with that index.

Am I missing something completely obvious?

Thank you!
 
This is expected - it will run the number of times your task returns success plus one. If you change CanExecute to always return false it will only run once. The reason for this is that the tree keeps trying to execute the task until it can no longer be executed.
 
This is expected - it will run the number of times your task returns success plus one. If you change CanExecute to always return false it will only run once. The reason for this is that the tree keeps trying to execute the task until it can no longer be executed.
I've tried all kinds of combinations and do not understand how this works.

How I imaged this working is:

Option 1:

1. CanExecute is run
2. If a real job ISN'T available, I set the currentJobIndex to JobType.Wander
3. Return true, since we can guarantee some job will execute (Wander in this case)
3. The Wander task gets executed and returns success
4. The tree is finished, starting all over again

Option 2:

1. CanExecute is run
2. A real job IS available
3. Fetch that job, set the currentJobIndex to it
4. Return true, since we can guarantee a job other than Wander will execute (in this case, ChopWood)
5. The fetched job gets executed and returns success
5. The tree is finished, starting all over again

I thought that doing it this way, in both cases, we would return true, because we'll either have a real job ready, or we will launch the wander task.

No matter what combination I try, I always get CanExecute running twice (when I need it to execute once, set which job will be done, and return true to be able to run it).

How could I achieve this desired behavior? Here's my current tree structure:

Screenshot_1.png
 
Last edited:
Because CanExecute can run multiple times it should not have any logic for determine what can execute. You should instead determine the next index that can execute within OnChildExecuted. This will allow you to have CanExecute run as many times as it needs to and it won't change the results of what should execute next.
 
Because I need to check for which job will be executed at the very beginning, I now do this immediately on CurrentChildIndex instead of OnChildExecuted. That way, the job is determined before running any of the tasks. I hope this is a good way of doing it for my use case?

This does however leave OnChildExecuted empty and CanExecute constantly returning true which makes no sense, but that's just because of my current design, which should probably be changed anyway.

Thank you for the swift help as always.
 
Top