Skip to content

Resume behavior with selectors #2

@ncblakely

Description

@ncblakely

First off, thanks so much for this library! I'm new to behavior trees, but have successfully implemented this in my game and have found it to be very well designed and intuitive. It's a shame it hasn't gotten more attention.

Quick question about the intended behavior of the "resume" feature the TreeState object provides when dealing with a tree that has a Selector node. When I have no Selectors, resume works pretty much as I'd expect -- the next iteration always restarts at the last node that returned Status::RUNNING.

However, when I introduce a Selector into the picture, the resume behavior changes. Take this minimal repro of my situation:

struct TreeContext
{

};

#define TRACE_METHOD() printf("In %s\n", __FUNCTION__)

bool Leaf1(TreeContext& context)
{
    TRACE_METHOD();
    return true;
}

bool Leaf2(TreeContext& context)
{
    TRACE_METHOD();
    return true;
}

Status Leaf3(TreeContext& context)
{
    TRACE_METHOD();

    return Status::RUNNING;
}

Status Leaf4(TreeContext& context)
{
    TRACE_METHOD();
    return Status::SUCCESS;
}

bool Leaf5(TreeContext& context)
{
    TRACE_METHOD();
    return true;
}

bool Leaf6(TreeContext& context)
{
    TRACE_METHOD();
    return true;
}

bool Leaf7(TreeContext& context)
{
    TRACE_METHOD();
    return true;
}

int main()
{
    Builder<TreeContext> builder;

    Tree<TreeContext> tree = builder
        .selector()
            .sequence()
                .leaf(Leaf1)
                .leaf(Leaf2)
                .leaf(Leaf3)
                .leaf(Leaf4)
            .end()
            .sequence()
                .leaf(Leaf5)
                .leaf(Leaf6)
                .leaf(Leaf7)
            .end()
        .end()
        .build();

    TreeState state = tree.make_state();

    TreeContext context;
    while (true)
    {
        tree.process(state, context);
    }
}

With no Selector, one process() tick will stop at Leaf3, and the next will resume from there.

With the Selector however, it will start at the beginning every tick. Diving into the source code reveals that this is because the Selector node itself overwrites the resume index set by Leaf3 as the nodes are processed recursively.

Is this the intent? In my example, I wanted Leaf3 to be processed on the next iteration, bypassing the Selector. Intuitively, it seems to make sense for the Selector to only be re-processed when not resuming in the middle of a Sequence, but I could have entirely the wrong idea here as I am new to behavior trees. :)

Of course, I realize I could cause what I want to happen by adding conditions to Leaf1, Leaf2 that cause them to be effectively skipped in this scenario, but I am curious if this is a bug or intended behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions