Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pausing the behavior tree #29

Open
lulugo19 opened this issue Feb 3, 2020 · 8 comments
Open

Pausing the behavior tree #29

lulugo19 opened this issue Feb 3, 2020 · 8 comments

Comments

@lulugo19
Copy link

lulugo19 commented Feb 3, 2020

Hi there,

is there a way to stop the execution of the behavior tree temporarily and then start it again.

I have different kind of enemies which behaviors are controlled by behavior trees.
When they get hurt, I want to stop the behavior tree for a moment so that a hurt animation can be played.

@meniku
Copy link
Owner

meniku commented Feb 3, 2020

You should be able to stop it via the Root-node of the tree by calling the Stop() method and start it again later with the Start() method.
This will cause the BT to begin it’s traversal from the root, which should be ok in most cases ( as the BB will still remain the same ).

The way I do such things is usually to have a Selector below the Root node and something like a "is-stunned-branch" at the very left. This branch has a decorator that will have a stop rule for any lower priority branches and is active until the stun is over. That way I don't have to start/stop the entire tree.
In the end it's effectively the same result, so pick whatever you prefer.

@lulugo19
Copy link
Author

lulugo19 commented Feb 4, 2020

What do you mean with:

( as the BB will still remain the same )
?

What does the BB stand for?
I tried the approach stopping the tree with the Stop() and Start() method, but It's not quite what I am looking for.
I don't want to restart the behavior tree.
I have this pathfinding node that depending on the parameters lets the enemy run to a specific position, e.g. run to a player target position or run to a cover position.

In my tree there is a sequence of first going to a player target position and then later going to a cover position.
When the enemy is hurt when going to a cover position I dont't want the tree to restart so that the enemy is still going to a cover position (and not to a target position) when he's hurt.

When the enemy is hurt, I want to stop the current executing node, pause the tree and then immediatly restart the node again. Is this possible?

@meniku
Copy link
Owner

meniku commented Feb 5, 2020

sorry, with BB I mean the Blackboard.

When the enemy is hurt, I want to stop the current executing node, pause the tree and then immediatly restart the node again. Is this possible?

You could just do the set-up I suggest above within every subbranch of your tree instead, however it looks like you look for some kind of "pause" functionality.
This isn't something NPBehave has implemented yet, there may be some ways without changing the library itself ( for example using a custom clock instance etc ), but I'll recommend you to implement that functionality right into NPBehave itself instead.

I suggest you to add something like a virtual Pause and Resume method on the Node level that will call recursively down it's children.
You will need to override the Pause and Resume method on the following nodes:

  • Root: delegate the call to the mainNode node
  • Task:, where you will need to call Stop() for Pause and Start() for the Resume functionality
  • Composite: this will be the most tricky one, as every composite works quite differently, however to make it work for all composites, the easiest would be to invent some flag as member variable, like "IsPausing", and set this to true when Pause is called. Then iterate over all Children, check which ones are running ( State == CurrentState.Running ), and delegate the call to just them. In addition you will need to adjust the DoChildStopped(Node child, bool result) On every subclass (or refactor it with some wrapper so you don't need to), and execute the logic there only if the IsPausing flag is not true, when it is true, you will have to remember the stopped child in some list in the Composite class. In the Resume method you will set the IsPaused flag back to false, iterate through your list of remembered nodes that were running before, call the Resume method and clear the list.
  • Decorator: this works quite similar to the Composite node, however the difference is that you will just have one child to take care of, the Decoratee node.

I guess something like that basically should work, however there's a couple of additional things to keep in mind:

  • The Blackboard contents should not be changed from the outside of the tree while the tree is paused, as this could trigger some decorators to execute
  • The Clock would still run ( impact cooldowns etc ) - You shouldn't stop the clock ticking as long as you use the shared instance for all BTs
  • it would be clean to have a new state enum value Paused on the Node.State enumeration
  • For some decorator classes you may have to implement special treatment in the Pause / Resume function as well, as some of them have timers running and/or notifications registered to the blackboard that lead the tree to execute ( or stop it without stopping the child node )

@lulugo19
Copy link
Author

lulugo19 commented Feb 5, 2020

Thank you very much for the elaborate explanation how I can implement this functionality.
This is amazing. I am going to implement it.

@lulugo19 lulugo19 changed the title Stopping execution of behavior tree Pausing the behavior tree Feb 5, 2020
@lulugo19
Copy link
Author

lulugo19 commented Feb 5, 2020

Nice, your suggestions worked very well. It's exactly how I wanted to have it. Thank you very much.

Instead of making your suggested changes in the Root, Composite and Decorator class I just could change the common parent class Container. There I could also put the logic to only call DoChildStopped(Node child, bool result) when the container is not paused. So I haven't had to adjust it on every single subclass.

I changed only the following decorators to stop their timers / observing when paused:

  • ObservingDecorator
  • Service
  • WaitForCondition

Do you think it's enough?

I opened a pull request. So you might want to consider adding this to the libary.

@meniku
Copy link
Owner

meniku commented Feb 5, 2020

wow that was quick! glad that my suggestion worked :)

You'll need to give me some time to review your changes, write some tests and potentially update the readme.

@lulugo19
Copy link
Author

lulugo19 commented Feb 6, 2020

I have written some tests. Noted a problem. It's explained in the pull request. I need your help on this.

@meniku
Copy link
Owner

meniku commented Feb 6, 2020

oh that's really cool! thanks. I'll check the P/R

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants