NodeCanvas Forums › General Discussion › JRPG Battle System
Hi all,
Since i am still new on NC, can someone suggest me on how to make a turn based rpg battle system on NC (e.g. Final Fantasy 7)
Also, any suggestion on when to use BT and FSM?
As i understand it:
– BT is better suited for AI purpose like in enemies, or things that should decide overtime
– FSM is ideal for stateful system, i think i should use FSM to make what i want (?)
Currently i also read the source of NC to understand things. It is really an elegant system, kudos for the dev
Hello,
BTs are very useful for decision making while FSMs are really good in decision making unless the FSM becomes very complex almost spagetti and even then, BTs excel in decision making. By decision making I mean that based on a nunmber of micro factors, a final decission (action) is chosen and executed.
FSMs are great when there are very dinsticive broad states of a behaviour/logic, like for example Attacking, Defending, Healing, Fleeing, Idling etc and the means of transitioning from one another is also quite broad. Do not try to represent a state as an action. ‘Attack’ is an action. ‘Attacking’ is a state. This is the simple rule of thumb I think when it comes to it.
So, in your case, you really want an NPC to decide what to do in his turn, based on a number of factors. Most probably there will be a single decided action in your case: Drink Healing Potion, Attack Cloud, Guard etc. So in my opinion a BT is far more flexible in this case, or in other words, you should use a BT for that 🙂
Now, there are some things to take notice for when you want to use a BT for turned based decision:
– You probably need to set the option on the BehaviourTreeOwner to ‘OnEnable:DoNothing’.
– You don’t want the BT to “Run Forever” in update, so you should uncheck the option “Run Forever” in the inspector of the BehaviourTreeOwner.
What this will do, is that when the BT is executed, is will be executed only once and then stop, which in practise can represent the “turn” for the NPC.
So in code, you can manualy start the BT by calling BehaviourTreeOwner.StartBehaviour when it’s the NPC turn to decide and do. You can even get a callback when that behaviour tree (turn in this case) is finished (so for example continue to the next NPC) by using an overload of the StartBehaviour method:
GraphOwner.StartBehaviour(System.Action callback);
Let me know if you need more help on the matter or code examples.
Cheers and thanks,
Gavalakis Vaggelis
Join us on Discord: https://discord.gg/97q2Rjh
Wow, thanks for the headup, i learn a lot. Still FSM and BT looks like quite interchangable since i havent dabble too much with BT
So i got more questions:
1. Is it FSM or BT more suitable for entire battle system? (who control all the turn based thing). For example i have three heroes fighting three enemies, and depend on their speed stat, the current turn will be given to one of them. The battle system will continue until either all enemy died or all heroes died.
2. I also want to define attacking animation sequence. For example: first play “slash” animation, then spawn a particle , after 1 second zoom camera on enemy, spawn another particle on selected enemy and display damage, then do next turn.
What is more suitable for sequence like this?
You are welcome.
Sure:
1. You can use a behaviour tree for each enemy to decide what action to take in it’s turn. When the enemy finish his action you can then move to the next enemy by executing it’s behaviour tree. So, I would use BTs for that.
2. Well, in the BT context the way to create a sequencer is by using the Sequencer node and have each action as a child node to that Sequencer :). NodeCanvas comes with a nice feature of using an Action List though. So instead of having a whole lot of different nodes under a Sequencer, you can have a single Action List node and all your actions related under that list. So it’s far more managable this way, since if you want to move all the actions to another branch for example, you simply move the Action List.
Cheers!
Join us on Discord: https://discord.gg/97q2Rjh
Hello,
1. When i want to go next turn. After i finished one enemy, i want to choose next enemy or hero depend on who fill his “loading time” first. Should i use another BT to control this process before calling specific BT on enemy or hero to process his turn?
2. Cool, i will try BT to sequence effect
Wow so BT could handle a lot of things, looks like FSM doesnt have much love there :p
Hey,
Yes, BTs are far more powerfull for decision making that FSMs 🙂
Regarding your question,
I would personaly handle this in code since creating a Battle Manager BT, although its certainly possible, I think it will get complicated especialy due to the fact of “registering” combatants.
Furthermore there is nothing there really that has to be decided on a number of facts, other than their Initiative Time.
So, personaly I would create a Battle Manager class, which on initialization will register all combatants along with their initiative score, do the math required (based on your game) and execute the combatant’s BT in order of their score if it’s an NPC or simply give control to the Player if it’s not. Or something similar to that 🙂
Some things are just better handled in code 🙂
Join us on Discord: https://discord.gg/97q2Rjh
Hi Gavalakis, sorry for reviving old thread
To learn NC, I will try making Battle Manager without code 😀
So now i have a game object named “Battle Manager” (BM):
– BM has a blackboard variable called “enemies” which is a list of enemies. It is actually a List<Component>
– Each enemies has behavior owner component, uncheck run forever, do nothing on enable and on disable
– The aim is iterating enemies and with “graph owner control” action, BM will start behavior on each enemy
– The question is how can i wait completion of “graph owner control” on enemies?
Hello,
No worries at all.
Here is the ‘Graph Owner Control’ code revamped, so that you can wait for the behaviour to finish like you describe. Simply replace the GraphOwnerControl.cs code with the following:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
using UnityEngine; using System.Collections; namespace NodeCanvas.Actions{ [Category("✫ Utility")] [Description("Start, Resume, Pause, Stop a GraphOwner's behaviour")] [AgentType(typeof(GraphOwner))] public class GraphOwnerControl : ActionTask { public enum Control { StartBehaviour, StopBehaviour, PauseBehaviour } public Control control = Control.StartBehaviour; public bool waitUntilFinish = true; protected override string info{ get {return agentInfo + "." + control.ToString();} } protected override void OnExecute(){ if (control == Control.StartBehaviour){ if (waitUntilFinish){ (agent as GraphOwner).StartBehaviour( delegate{EndAction();} ); } else { (agent as GraphOwner).StartBehaviour(); EndAction(); } } } //These should take place here to be called 1 frame later, in case target is the same agent. protected override void OnUpdate(){ if (control == Control.StopBehaviour){ (agent as GraphOwner).StopBehaviour(); EndAction(); } else if (control == Control.PauseBehaviour){ (agent as GraphOwner).PauseBehaviour(); EndAction(); } } } } |
Iterating all enemies as you describe can certainly be one way of doing this, so that enemies take turns in order.
Let me know if you have any further questions 🙂
Cheers!
Join us on Discord: https://discord.gg/97q2Rjh
Thankyou so much
I suspect i should change graph owner control code a litle too before.
On standard code. When i use iterate v2 and put $enemy as my dynamic var. I just cant select that $enemy as agent on the action. I must use explicit blackboard var. Why is that?
Hello,
When you save something as a dynamic var, the only way to read it back is also through a dynamic var, since those variables are created in runtime, and (at least currently) I don’t keep track of the dynamic variables entered in editor time.
Now, specificaly for the agent override dropdown, I haven’t added the ability to select (type in) a dynamic var, mostly to avoid accidents, since a dynamic var is not really type safe. I could add that in the next version though 🙂
So, essentialy right now, an agent can be overriden only explicitely unfortuntately. Again, this is purely an Editor UI restriction.
Join us on Discord: https://discord.gg/97q2Rjh
Hi Gavalakis,
Looks like on version 1.5.9w you made the GraphControl action can wait for completion. Thanks for that
So, yes, looks like i should add some custom action and custom condition tasks for my battle system. I will use NC for general workflow and enemy decision then.
I have questions:
1. What if i want to use Graph Control action from FSM? FSM doesnt have set agent right?
Since Blackboard cant add a GraphOwner type variable. I add a custom variable type just for GraphOwner. Add a variable on blackboard, then i fetch it unit that will be processed next turn. Next i will do Graph Control action on that variable. Is it a good way to approach this problem?
2. I want to define an enemy, this enemy will have common stats like HP,ATK, etc. Should i create a Monobehaviour script and use Script Control to get data, or is it better design to make each enemy have blackboard with manually added variable?
3. Is blackboard can automatically save/load on application exit on 1.5.9w?
Hello there,
You are welcome 🙂
1. No the FSM doesn’t have something similar like Set Agent Decorator in BTs. The way is indeed to override the task agent like you do.
By the way, the blackboard CAN have a GraphOwner variable, but through the use of the UnityEngine.Object variable. By pressing the small button on the right of the variable field in the blackboard, you are able to set the target type. A list of all the types configured will show up and as you will see, GraphOwner is not there. To make it apear, you can open up “Widnows/NodeCanvas/Configure Types”, and at the bottom of the window opened press “Add New Type” and select “Classes/Assembly-cSharp/NodeCanvas/GraphOwner”. From now on, GraphOwner type will show up in the list for this project 🙂
It’s kind of an obscured feature.
2. I would prersonaly suggest to create a MonoBehaviour for each of your units so that you can manage the code better and use NC to interface with that MonoBehaviour. This way, you are more free to design the game however you like without NC getting in the way, but instead helping you. In the upcomming version there is also a very nice new feature to link blackboard variables with your Monobehaviour 😉
3. The automatic Save/Load of blackboard has been removed from the UI, but you can very simply create a MonoBehaviour to do that. A very simple example:
1 2 3 4 5 |
void OnDestroy{ GetComponent<Blackboard>().Save(); } |
Cheers!
Join us on Discord: https://discord.gg/97q2Rjh