NodeCanvas Forums › General Discussion › Turn-based game – turn-based execution of Actions
Hello,
We are currently evaluating NodeCanvas for using it in a Turn-based game. So far we love the library. But we are having problems to implement it for a turn-based game.
Each time the player presses the Turn button, a new turn is processed, along with the AI. We have inserted the BTO.Tick() method in our OnTurn() method.
Everythin seems to work fine, except for Actions that return the Running status. In this case, the Action is evaluated on every single frame. We don’t want this, we want to run it on every turn, this means, it should only run when the Tick() method is invoked.
Is there any easy way to prevent the Actions from running every single frame?
Thanks a lot!
Javier Otaegui
CTO
NGD Studios
Ok, I think I have a solution.
I modified MonoManager.cs and deleted the Update method, and added a public Tick() method with the same contents.
I manually run MonoManager.current.Tick() from BehaviourTreeOwner’s Tick(), just before the BehaviourTree’s Tick().
Do you think there is a more elegant solution? Are you planning to support Turn-based games? I rather not modify your code, althought these are very small changes.
Thanks!
Javier
Hello!
Let me first give some information.
Right now, the BehaviourTree’s flow and the Action Tasks are running in paralel. What this means is that when the BehaviourTree is Ticked once, the tree is evaluated. If an Action Tasks is found that needs more than 1 frame to complete, it registers itself in the MonoManager. That action is then updated until it is complete regardless of how often the BT is Ticked. On a later BT Tick, the tree checks the Action’s return status and proceeds accordingly.
This allows for even if a BT is ticked infrequently, that the actions started do update in their own timing.
If Action update was done per Tick of the BehaviourTree, then an action that plays an animation and returns Running until that animation is done, would look stepped if not Ticked constantely, while now a single Tick of the action suffice for it to complete.
On the matter at hand (turn-based), the way I would go about it, would be to set the BehaviourTrees to RunOnce and then call BTO.StartBehaviour(). What this means is that the BT will be evaluated once and keep ticking until it’s root status is either Success or Failure (which means no action is returning Running).
An overload of the StartBehaviour can also take a System.Action callback parameter. This will be called back when the tree is done.
So as a practical example lets take an X-COM style game.
After the player finish his turn, you can call BTO.StartBehaviour(OnFinish), on the first enemy unit.
The unit’s BT can then decide on the unit’s behaviour and then execute this behaviour (PlayAnimations, Movement etc). When the BT is finished, OnFinish will be called where you can pass the turn to another AI unit (by executing it’s own BehaviourTree) or back to the player.
So the BehaviourTree here in essence represents a whole unit’s turn. Decision and execution.
I know you didn’t asked me all this, but I am trying to understand if I am missing something by explaining and suggesting a workflow 🙂
Cheers
Join us on Discord: https://discord.gg/97q2Rjh
Thank you very much for your answer. I understand what you explained that they run in parallel and it makes sense.
The thing is that our game is strictly divided in Back-end and Front-end, and we will not be using BTs for anything animation-related. Our Back-End knows nothing about Unity or rendering, and it could run as a stand-alone program if needed.
This means we need to Tick the BT just once per turn. It makes no sense for us to tick it per frame as the game state does not change. Also, we don’t need the Actions to run per-frame, as there’s nothing they can do as the game state is the same. They only need to be run once per turn, just as the BT. Turns are triggered by the human player with a TURN button.
Right now the solution I suggested is running fine, but I wanted to know if there is a different suggestion from you.
Thanks!
Javier
Hey, sorry for the late reply :/
So after all, I have another solution for you if you want to. It’s a bit more elegant, but you still need to change the source code unfortunately. On the other hand, I will try and add this as some kind of an internal option.
Let me know..
Cheers 🙂
Join us on Discord: https://discord.gg/97q2Rjh
That’s great. Would love to hear about the more elegant solution!
Thanks!
Javier
Hey,
So, after all I don’t know if it’s actualy more elegant since it requires more source changes but here they are:
In ActionTask.cs make the UpdateAction method public.
Also remove any MonoManager.current.AddMethod and RemoveMethod (found in ExecuteAction, EndAction & PauseAction).
In BTAction.cs, add action.UpdateMethod() in OnExecute right before the return.
That’s it. So you completely bypass the MonoManager and the action is updated every time the action node is ticked.
I haven’t thoroughly tested it but it works. This will only work for BTs of course if thats the only system you are using.
On the other hand, if you don’t want running actions, why don’t you just not use action that need more than a frame to complete?
If you create your own actions and you call EndAction within OnExecute of the action then what you are after will happen already.
Join us on Discord: https://discord.gg/97q2Rjh