News Forums RAIN General Discussion and Troubleshooting Behavior Tree Question

This topic contains 6 replies, has 2 voices, and was last updated by  strangegames 4 months, 3 weeks ago.

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
    Posts
  • #39436

    strangegames
    Participant

    I’m still a little confused about some aspects of behavior trees. From what I understand, they execute once per update cycle so I’m a little confused as to what repeating does for a node. Some of the example trees have detect nodes at the top that are set to repeat. It seems that this is unnecessary since the entire tree runs once per update and these will always run at the start. Does the repeat mean that the node repeats during pass through the tree? If so, it seems like you could have repeats while waiting for a certain condition that could cause the tree not to exit. Surely this wouldn’t cause the game’s update cycle to hang if the tree never exited.

    #39442

    Sigil
    Keymaster

    You are correct that a behavior tree updates every frame. Incoming wall of text.

    So normally when a node is executed it returns success or failure. If it is a decision it often returns success or failure based on what its children return. Since you sometimes need to hold execution in the tree for awhile, a node can also return running, which tells its parent to come back next update and try again to get a success or failure.

    In RAIN, if a behavior tree returns success or failure, on the next update we reset the tree and start over. If a tree returns running though, on the next update we walk through the tree, following all the running nodes until we get to the last running node. At that point we try to get a success or failure again from the node and keep going (it can return running again if it likes).

    When you mark a node to repeat, it tells the node that instead of returning success or failure, it should return running and start again the next update. In the case where the repeating node was going to return running anyways, we allow that through, and whatever child node returned running will continue on the next update.

    Since a tree always returns (either success, failure, or running) it can never halt the update loop, the only thing that can do that would be a custom action that never returns for whatever reason. Repeating nodes can, however, stop the remaining execution of your behavior tree, which might make your AI halt in a way.

    An example:

    root
       sequencer
          sequencer
             timer (seconds: 3)
             expression (expression: debug("3 second timer expired", returns: success)
          sequencer (repeat: forever)
             timer (seconds: 6)
             expression (expression: debug("6 second timer expired", returns: success)
          sequencer
             timer (seconds: 9)
             expression (expression: debug("never going to get here")

    So this would pause 3 seconds and output and then pause 6 seconds and output, and then pause 6 seconds and output, and continue that forever.

    #39443

    strangegames
    Participant

    Thanks for the quick and informative post. So when you say it walks the tree to find the last running node, I assume that means it doesn’t execute any nodes except the last running one. Is that correct?

    Also, I noticed that some of the examples have detects at the top of the tree set to repeat. Does this mean that if another node further down in the tree is returning “running”, these detect nodes will still get evaluated on each pass?

    Third question, in a node set to repeat, can you break out of that node by having a child return false?

    Thanks again,
    Reggie

    #39446

    Sigil
    Keymaster

    1) Yes, when it goes to the last running node it is just skipping over the nodes since they are all still waiting on that descendant to finish.

    2) Usually when there is a tree that has a detect high up that is repeating, that detect is also in a parallel. So one aspect of a parallel is that it will continue to execute all children that return running on each update until its exit condition is reached. The exit conditions default to fail: any, succeed all, which means the parallel will return failure if any of its children return failure (interrupting any other children still running), and will return success only if all of the nodes return success. So that was a long yes, those detect nodes will get evaluated on each pass, but due to the behavior of the parallel and not so much the repeat.

    3) Not unless the repeat was set to repeat until failure. The only way to stop a node that is repeating forever is to interrupt it, which will occur if a parallel higher up in the tree meets its exit condition, or if a constraint higher up in the tree fails.

    #39449

    strangegames
    Participant

    Ok thanks, that clears up a lot.

    One last question on the “(interrupting any other children still running)”. Does that mean they will stop running after this pass through the tree or does that mean they will be stopped immediately on this pass even if they haven’t finished going all the way through their branch? I don’t know if the parallel nodes actually execute the children at the same time where a short branch might return false while a longer branch is still running.

    Thanks again,
    Reggie

    #39450

    Sigil
    Keymaster

    Since the behavior tree isn’t actually threaded and can’t really run in parallel, all running children will be updated that one last time before the parallel “interrupts” them. The interrupting part is that even if a child returns running at that point, the parallel is done and is going to return failure or success, so the execution will never return to that child in its current state and the next time around it will get reset.

    root
       parallel (fail: any, succeed: any)
          sequencer
             timer (seconds: 3)
             expression (expression: debug("Parallel is going to stop now"), returns: success)
          sequencer
             timer (seconds: 6)
             expression (expression: debug("I am never going to get a chance to run"), returns: success)

    After three seconds the first timer is going to return success, and since the parallel is set to succeed if any of it’s children succeed, it will return success after that happens. The sequencer with the 6 second timer will get updated one last time (the same update the 3 second timer returns success on) but unless you had some incredible lag for that update it won’t get a chance to finish.

    • This reply was modified 4 months, 3 weeks ago by  Sigil. Reason: fixed a bug in the behavior tree
    #39452

    strangegames
    Participant

    Got it. Thanks for all the help!

Viewing 7 posts - 1 through 7 (of 7 total)

You must be logged in to reply to this topic.