0
\$\begingroup\$

I'm trying to figure out how to return one result to the immediate parent, while returning a different one to the grandparent. The idea is: given a "turnToFaceTarget" tree, skip the rotation if already facing the right direction, and continue doing whatever's next. This means the condition node should return Failure to its parent (turnIfNeeded), but the parent should return Success to its parent (walkToPosition). How can we accomplish this?

I thought of using an invert decorator, but I'm not sure how to implement it, since no nodes don't have a parent reference. I can think of ways to hack it, but... how is this "supposed" to be done?

walkToPosition:
  sequence("walkToPosition") {
    turnToFaceTarget // this should return Success when done turning
    action("startWalking") {
    }
  }

// returns Success if/when facing the right way, and Running while it's turning
turnToFaceTarget:
  selection("turnIfNeeded") {
    condition("shouldTurn") {
      // We want to optionally skip rotation (return Failure), but return an overall Success
      return currentAngle == targetAngle ? Failure : Success
    }
    sequence("turn") {
      action("setRotationVelocity") {
      }
      action("rotate") {
          // apply rotation
          return targetAngle == targetAngle ? Success : Running
      }
    }
  }
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

First of all, I think you don't need this. If you set the rotation velocity to zero, and then the action verifies that the angle matches, it should be effectively no rotation and it sucedes.

I believe you should make your actions more complex. For example, you can set the target angle, and then set the rotation velocity in such way it does not overshoot (knowing the delta time). In fact, I would probably make "turn" an action instead of two actions in a sequence. I think you are being too granular.


Yet, for the sake of argument let us say that rotation is expensive, and we want to optimize it away, and the tool we have is behavior trees.

Well… This is what I imagine:

selection("turnIfNeeded") {
    condition("shouldNotTurn") {
      return currentAngle == targetAngle ? Success : Failure
    }
    sequence("turn") {
      action("setRotationVelocity") {
      }
      action("rotate") {
          return targetAngle == targetAngle ? Success : Running
      }
    }

I'm guessing condition just executes the condition, but action goes and does something else elsewhere.

Notice I changed "shouldTurn" to "shouldNotTurn" with swapped Success and Failure.

So when the angle is at its target, "shouldNotTurn" succeeds, and so the selector succeeds (and it is done turning). If the angle is not at its target, "shouldNotTurn" fails, and so the selector goes to the next item which is the "turn" sequence (which only sucedes when it is done turning).

Isn't that what you want? You want to invert he condition, then invert the condition.


About how to do an invert. What I do - instead of using decorators - is have an negated sequence and a negated selector.

There are two things a node can be looking for Success or Failure, and there are two things a node can return when it is done Success or Failure. So that is two by two for a total of four cases.

The negated sequence and negated selector complete the set:

  • Selector:

    Is looking for a Success. Returns Success when it finds it, Failure otherwise.

  • Negated selector:

    Is looking for a Success. Returns Failure when it finds it, Success otherwise.

  • Sequence:

    Is looking for a Failure. Returns Failure when it finds it, Success otherwise.

  • Negated sequence:

    Is looking for a Failure. Returns Success when it finds it, Failure otherwise.

If you need to make an invert, then use either of the negated versions with only one child. And no parent reference is involved.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .