NodeCanvas Forums › Support › Tick() broken for non-repeat graph without actions running over time
Hi,
Updating Node Canvas to the latest version broke my existing logic and I’ve been digging into what happens.
My setup is very similar to the section in the documentation called “Manual Tick (BehaviourTrees only)”
– I instantiate a BehaviourTreeOwner at runtime
– I set its “behaviour” property to the behavior I want (obtained via inspector variable)
– I set it to not repeat
– Then I call Tick() on that owner from Update
What seems to happen is that my graph is never technically running, since I don’t start it, and it executes in one frame (it doesn’t contain running actions, all actions it contains return Success right away).
The updates to BehaviourTree.cs and BehaviourTreeOwner.cs that I can see from my previous version and the latest one seem to no longer directly call Tick on the behavior but instead use UpdateBehaviour. However, this leads to a check if graph.isRunning == true before actually calling GraphUpdate(). Since my graph is not runnning, this check fails and it will never actually update the graph when I call Tick().
Could you please either provide a fix for this, or let me know what the new correct way of achieving that result is?
My code is
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// Called once on awake public virtual void Initialize() { owner = Parent.gameObject.AddComponent<NodeCanvas.BehaviourTrees.BehaviourTreeOwner>(); owner.behaviour = behaviourTree; owner.blackboard = Parent.sharedBlackboard; owner.repeat = false; owner.enableAction = NodeCanvas.Framework.GraphOwner.EnableAction.DoNothing; owner.disableAction = NodeCanvas.Framework.GraphOwner.DisableAction.DoNothing; } // Called every frame public virtual void Execute() { owner.Tick(); } |
Thank you
Hello,
Thanks for finding and letting me know of this bug.
A quick fix/solution to that (in the latest version), would be to make a call to “StartGraph” with the parameter “autoUpdate” being false in your Initialize method. In that same ‘StartGraph’ overload that takes the “autoUpdate” parameter, you can also provide a callback for when the behaviour is finished. You can if you want use that callback to also stop the graph when that is done.
Here is an example code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// Called once on awake public virtual void Initialize() { owner = gameObject.AddComponent<NodeCanvas.BehaviourTrees.BehaviourTreeOwner>(); owner.blackboard = gameObject.AddComponent<Blackboard>(); owner.StartBehaviour(behaviourTree, false, OnBehaviourFinish); owner.repeat = false; owner.enableAction = NodeCanvas.Framework.GraphOwner.EnableAction.DoNothing; owner.disableAction = NodeCanvas.Framework.GraphOwner.DisableAction.DoNothing; } // Callback OnBehaviourFinish void OnBehaviourFinish(bool success) { owner.StopBehaviour(); } // Called every frame public virtual void Execute() { owner.Tick(); } |
Please let me know if that works for you.
Thank you!
Join us on Discord: https://discord.gg/97q2Rjh
Thank you, for now I just commented out the check for IsRunning as I’m not using graphs extensively and don’t have any graphs that will run more than a frame so it won’t break anything yet.
I believe I’ve tried something similar to what you suggest while playing around and looking into the source code, but since my graph stops right away, starting it just gives me one frame of execution before it’s no longer running again. I noticed that adding a node that runs over multiple frames (e.g. a debug log for 2 seconds) would allow the graph to update for those 2 seconds then stop.
I’ll leave it like that until my next update and hopefully by then a better fix will have been released for this use-case. Thank you
Hello again!
I updated to the latest version of NodeCanvas and the problem is back so I have to repeat my local hacky fix again, and figured I’d point out the issue once more in hope for an official fix.
Here’s how I’m using Node Canvas:
This used to work just fine when I initially created this setup, but after upgrading NodeCanvas a few months ago, it is no longer executing my BT each time I Tick() it, only once the first time.
In CanvasCore/Framework/Runtime/Graph/Graph.cs, I have to remove the check for isRunning == true
in the UpdateGraph function to make my BT work, as a graph that doesn’t start any longer-than-one-frame action will apparently only “run” for one frame, and so Tick() will not execute it past the 1st frame (where it actually starts and gets executed once).
You suggestion from a few months ago does not help, the graph still isn’t considered to be running after the initial frame it ran and completed on, so Tick() does nothing.
Repro steps:
– Create a new project
– Import NodeCanvas
– Create a new GameObject
– Add a Behaviour Tree Owner to that object & set it to not repeat and do nothing on Enable/Disable
– Create a bound BT for that owner that simply logs a variable value from the blackboard
– Create a MonoBehaviour on that same object that contains the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class TestScript : MonoBehaviour { public NodeCanvas.BehaviourTrees.BehaviourTreeOwner owner; // Use this for initialization void Start () { owner.StartBehaviour(); } // Update is called once per frame void Update () { owner.Tick(); } } |
What happens:
– The value of the blackboard variable is logged once
What I expect:
– The value of the blackboard variable is logged each frame
This would work:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Use this for initialization void Start () { //nothing } // Update is called once per frame void Update () { owner.StartBehaviour(); owner.Tick(); owner.StopBehaviour(); } |
but means it won’t work for any graph that lasts more than one frame.
Could you look into this issue please, and either support that functionality again, or let me know of an alternative supported way to achieve that result?
I guess the old Tick() behavior was “start or update” and the new one is “update if running” which is causing my issues. I’d just like to be able to update NodeCanvas without having to reapply fixes each time to maintain the functionality of the NodeCanvas version I started my project with.
Thank you
EDIT:
I guess I was a bit caught up on making sure Tick() worked like it did before, I can do this I guess:
1 2 3 4 5 6 7 8 9 10 |
if( !owner.isRunning ) { owner.StartBehaviour(); } else { owner.Tick(); } |
Let me know if you see any issues with that approach, thank you
Yeah, I came here to say I have the exact same problem. Why has this not been fixed yet? :\ I’m using the latest version from the asset store (2.9.6).
For now I’m using the fix that Clem posted.
Hello,
Aparently I completely missed Clem’s last post. I am really sorry about that!
Tick() should always be called after a graph is Started (regardless of whether or not the behaviour is repeated). StartBehaviour must be called since it is responsible for setting references as well as calling various callbacks in nodes like OnGraphStarted (which are essential for correct initialization).
If we want to manually Update a graph (or Tick a tree in case of BT), we need to call StartBehaviour with the autoUpdate argument set to false owner.StartBehaviour(false, null);
.
In cases where we manually Tick the tree, leaving the “repeat” option enabled is desirable, since the point of the “repeat” option is to automatically Stop() the tree (disable it). Thus if we do not want any automation like that, we need to keep the “repeat” option enabled.
As such, to manually Tick() a tree correctly, all we have to do is leave the “repeat” option enabled and do something like this in code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using UnityEngine; using NodeCanvas.BehaviourTrees; public class NewBehaviourScript : MonoBehaviour { public BehaviourTreeOwner owner; void Start() { //we need to start the behaviour with autoUpdate set to false owner.StartBehaviour(false, null); //leaving repeat to true will keep the tree alive and enabled owner.repeat = true; } void Update() { //updates the tree once per frame. Tick also returns the root status. owner.Tick(); } } |
I have just updated the documentation (which was missing the part that StartBehaviour needs to be called). I have also now added a warning if UpdateGraph is called without first having the graph started to avoid any confusion.
Please let me know if this works for you.
Join us on Discord: https://discord.gg/97q2Rjh
Hey, thanks for getting back to this. I’ve tried implementing your way, but I’m getting some odd behaviour. This could be because of my own AI logic though, so I’ll have to investigate.
But when I’m using your implementation, I’m getting a few warnings that doesn’t seem to lead back to any of my code?
1 2 3 4 5 6 7 8 9 10 11 12 |
(NodeCanvas Warning): Graph is already Active. UnityEngine.Logger:Log(LogType, String, Object, Object) ParadoxNotion.Services.Logger:ForwardToUnity(LogType, Object, String, Object) (at Assets/ParadoxNotion/CanvasCore/Common/Runtime/Services/Logger.cs:127) ParadoxNotion.Services.Logger:Internal_Log(LogType, Object, String, Object) (at Assets/ParadoxNotion/CanvasCore/Common/Runtime/Services/Logger.cs:118) ParadoxNotion.Services.Logger:LogWarning(Object, String, Object) (at Assets/ParadoxNotion/CanvasCore/Common/Runtime/Services/Logger.cs:60) NodeCanvas.Framework.Graph:StartGraph(Component, IBlackboard, Boolean, Action 1) (at Assets/ParadoxNotion/CanvasCore/Framework/Runtime/Graphs/Graph.cs:604) NodeCanvas.Framework.GraphOwner:StartBehaviour(Boolean, Action 1) (at Assets/ParadoxNotion/CanvasCore/Framework/Runtime/Graphs/GraphOwner.cs:111) NodeCanvas.Framework.GraphOwner:StartBehaviour() (at Assets/ParadoxNotion/CanvasCore/Framework/Runtime/Graphs/GraphOwner.cs:104) NodeCanvas.Framework.GraphOwner:Start() (at Assets/ParadoxNotion/CanvasCore/Framework/Runtime/Graphs/GraphOwner.cs:211) |
Hey,
You are welcome.
In the BehaviourTreeOwner inspector, you need to set the “OnEnable” setting to “Do Nothing”. Otherwise if it is set to “Start Behaviour”, the behaviour will be started automatically as normal and by default. However if in your case you want to start the behaviour manually (like the code provided) you don’t want the BehaviourTreeOwner to also start it. Thus setting “OnEnable” to “Do Nothing” will prevent this 🙂
Join us on Discord: https://discord.gg/97q2Rjh
Worked like a charm, thank you 🙂 !
You are very welcome 🙂
Join us on Discord: https://discord.gg/97q2Rjh