NodeCanvas Forums › Support › [Bug] Graphs are not fully reset when Stoping and Starting again
Tagged: bug
Description
My game creates small jobs for the characters in the game and enqueues them. These jobs are triggering behaviour trees which need to have one ActionTask at the end which internally calls (base.ownerSystem as BehaviourTree).Stop(); My system listens to the callback to immediately trigger the next job. To avoid performance problems I also implemented pooling for the graphs. That means that the same instance of a graph can be executed multiple times.
I now realized that the graphs behave unexpected when executed the second time.
here is the code of my ActionTask:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class FinishActivity : ActionTask { protected override string info => "<color=#AAFFAA>Finish!</color>"; protected override void OnExecute() { base.OnExecute(); Graph graph = base.ownerSystem as Graph; Debug.Assert(graph != null, "The Finished Task System must be a Graph (tested with BehaviourTree only)."); // base.EndAction(success: true); graph.Stop(); } } |
I try to explain steps to reprouce:
new BlackBoardSource()
as parentBlackboard and assign a method as callback.Result:
The graph behaves different in comparison to the first run.
// base.EndAction(success: true);
is commented:base.EndAction(success: true);
is not commented:Expected Result:
The graph has the same behavior every time it is stopped and started again.
Hello there,
Thank you for the details of reproduction. Stoping the graph from within its own execution is indeed a bit problematic. To make this work, please change your action code to call EndAction(null)
instead.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class FinishActivity : ActionTask { protected override string info => "<color=#AAFFAA>Finish!</color>"; protected override void OnExecute() { base.OnExecute(); Graph graph = base.ownerSystem as Graph; Debug.Assert(graph != null, "The Finished Task System must be a Graph (tested with BehaviourTree only)."); base.EndAction(null); graph.Stop(); } } |
EndAction(null) is used when we want to interrupt the action irrelevant of success or failure (which is relevant to this case since we stop the graph right after anyways), and will make the action completely reset to Resting.
You will still need to yield 1 frame just like you said before re-starting the graph in the onFinish callback (although I am looking into this and how to avoid this requirement).
Please let me know if the above change works for you.
Thank you!
Join us on Discord: https://discord.gg/97q2Rjh
Hi Gavalakis,
thank you for your reply and the potential fix.
This fix would only reset my custom action I guess. If my graph would use built-in actions or actions which require a return value, the graph would still be broken, or not?
Maybe not, because I did some further testing: It seems that the Finish Action is not reset properly because it stops the graph during execution of that action. If I manually reset all nodes (as done in Graph.Stop
) before I start the graph again everything looks fine.
So, the EndAction(null)
call might be the fix because it enables my Finish action to be reset.
Can you confirm this guess?
Hello again,
The EndAction(null)
is a special EndAction call used when we want to interrupt the action and reset it completely. It is also used from within nodes like Behaviour Tree Action Node; when the node resets it calls EndAction(null) to interrupt the action and reset it, since in these cases we do not care about success or failure (this is why manually resetting all nodes afterwards also resets the action). So is the same case when the graph is to be stoped from within the action, meaning that we do not care about whether the action ended in success or failure. We just want to end and reset it. Thus in these cases, the special call EndAction(null)
can be used.
Please let me know if this information helps.
Thank you!
Join us on Discord: https://discord.gg/97q2Rjh
Hi,
Unfortunately, your reply doesn’t answer all my questions.
What I was not sure about is not about my particular action but all actions in the graph.
So, when I call graph.Stop()
within an action it seems that all actions of the graph are reset except the one or ones which are currently executed. (Is this correct?)
Therefore, I have to reset the current action manually by calling base.EndAction(null)
.
But is this enough? Even if non-active-actions are properly reset what about other running actions when using a parallel composite node? I guess there could be actions which are also still running but I cannot call EndAction(null)
on them because I don’t know about them. So, I believe they would not reset and would keep running also during the next time I use the graph instance…
I did some tests but my suggestion turned out to not being true.
It seems that other running actions are reset properly.
But I found another bug: The composites are not reset. The sequencer containing my Finish-Action was marked Success (although i ended with null
and not true
) and at the second run just did not do anything anymore.
See the attached pictures for more information (the pictures are both from the second run).
EDIT #1:
When on the second run the left part has finished (61 seconds waiting time are over), the graph restarts and the right part is also run again.
EDIT #2:
If I reset the graph again manually every time before I start it everything seems to work.
I use this code:
1 2 3 4 5 6 |
foreach(var node in graph.allNodes) { node.Reset(); } |
I am not sure if this also works with sub-graphs though.
Hello again,
You are right. This is why the included action for stopping the behaviour (the action task called Control Graph Owner), does EndAction(null) and
graph.Stop()` after 1 yield frame. If you want to do the same in your custom action, here is how you could do it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class FinishActivity : ActionTask { protected override string info => "<color=#AAFFAA>Finish!</color>"; protected override void OnExecute() { StartCoroutine(YieldFinish()); } IEnumerator YieldFinish() { yield return null; EndAction(null); ( base.ownerSystem as Graph ).Stop(); } } |
Doing this will solve the issue where the sequencer (and other nodes in general) are not resetting correctly. Once again, the problem and special case here, is the fact that the graph is stopped from within its own execution. Stoping the graph otherwise (outside of its own execution does not need any of this).
Please let me know if the above works for you.
Thanks!
Join us on Discord: https://discord.gg/97q2Rjh