I’m trying to wrap my head around combining multiple Behavior Graphs to model complex behavior including exchangeable sub-behaviors, especially when used “at scale”, i.e. on tens or hundreds of instances.
Think a “brain” behavior graph for an enemy that models the base behavioral structure common to all enemies that can have different enemy-specific “plug-in” sub-behaviors like movement behaviors while idle (e.g. following waypoints / standing still), attack behaviors (ranged / melee) and so on.
I tried looking at the documentation and samples, but I feel those either only cover very trivial use cases or - in the case of the sample from the asset store that is often referenced in other posts here - are not self-explanatory with little documentation and have quite a few problems (random isolated branches that apparently don’t do anything (e.g. top left of graph “B_Brain_Patrol”, not connected to starting nodes, no references/values assigned), references I can’t trace back (see at the bottom) and graphs only applied to singular agents) that don’t exactly help understanding and make me question the usefulness of taking the time to really dig through it.
So far I have a “brain” graph handling common enemy states (the enum state-machine-like approach) triggering subgraphs for the sub-behaviors in states that can be handled differenty and using events to trigger state changes when e.g. detecting an enemy or losing sight of one.
As things grow I find myself wondering how to apply this to multiple different enemies:
Edit: answered in post #2 below: (Regarding Events: I see two ways of keeping events like “enemy detected” (where only the detecting enemy should react) separated for each individual enemy: either give each enemy its own event channel object OR use one single channel object and pass the enemy along with the message, then for an incoming event check attachedEnemy == Self. The former doesn’t scale well, the latter doesn’t feel that great either?
What’s the best way to get the uncoupled event handling the StartOnEvent nodes provide for many instances of a behavior graph? Should I use Event Channels only for cross-graph communication and model “instance-level events” like detecting an enemy as Wait for [Detection etc.] actions connected to a state switching action triggered when the Wait... returns Success (i.e. the “event” happened)? I like the way the Event handling can be centralized with the StartOnEvent node though, since something like enemy detection should switch to the same destination state from multiple states.)
Regarding Subgraph usage: I don’t understand how to best parameterize the sub-behaviors in subgraphs. Using the attack sub-behavior example, I can see multiple enemies using different behaviors (melee vs ranged attack) with different parameters (e.g. cooldown between attacks, damage, but also behavior-specific ones like should the ranged attacker reposition, what attacks should the melee attacker use etc.). Should I have exposed Subgraph and Blackboard variables on the “brain” behavior graph blackboard for every situation I hand off to a subgraph for and pass in the matching assets according to the wanted behavior for every enemy instance (possibly sharing Blackboard assets between enemies that should behave the same if the variables are only parameters to be read not modified)? This makes the most sense to me (assuming I can use a base Blackboard type variable and pass that to a subgraph vs needing to have the specific matching type?), but looking at the sample has me confused: In the sample I found one Run Subgraph node passing in a Blackboard (“B_StateMachine_Character” graph, “Run Attack Behavior” node all the way to the right), but I can’t figure out where that blackboard comes from: it says “BB_Attack”, but I can’t find an asset with that name or Blackboard variable in the B_StateMachine_Character graph. Also if I clear the reference and try to set it again from the dropdown, I can’t find it anymore either (I feel like there was a rename that wasn’t picked up? “BB_Character_Attack” should be it?).
Okay, I can answer the Events part of my question after some more experimenting and re-reading the docs: when creating an event channel and leaving the associated Blackboard variable empty, the “Behavior automatically generates the necessary event channel ScriptableObject .”, i.e. for event communication within a graph, the Behavior Agent running it creates and keeps track of the channel, only cross-graph communication requires creating and assigning the channel SO manually - that didn’t stick with me after my first documentation read-through.
To (hopefully) clarify the subgraph question:
How can I (from the outer behavior graph) parameterize a subgraph? Ideally, I would like to reuse the same “brain” behavior graph across different BehaviorAgent components, plug in different sub-behavior subgraphs into each along with subgraph-specific parameters (either directly from the BehaviorAgent component’s inspector or via a Blackboard I can pass in somehow).
It seems to me that I can’t have a BlackboardVariable that accepts any Blackboard asset (in which case I could have a pair of Subgraph- and Blackboard-BlackboardVariables assigned to a RunSubgraph node which I can change from the inspector to swap out the subgraph and associated parameters)?
Would I need a Blackboard per each sub-behavior-specific Run Subgraph node holding the parameters for all possible subgraphs that could be swapped into it via BlackboardVariable<Subgraph> (i.e. for the Run Subgraph node used for attack behavior, have a blackboard holding parameters for melee attacks, ranged attacks etc. so the actual subgraph can always read those it specifically needs and ignore the others)?
Yes exactly. You can use Run Subgraph Dynamically with a blackboard used as an interface for your sub-graph.
It is not currently possible to create a BlackboardVariable<Blackboard> to dynamically change the kind of a blackboard use by the Run Subgraph node.
Demo
Sorry about the confusion with the sample. You effectively ran into the 2 issues we identify post-launch, but we didn’t had time to update the demo since:
The top left nodes at the top of “B_Brain_Patrol” graph are leftover that should have been deleted. You can ignore those.
Almost as you guessed, “BB_Attack” was the first version before “BB_Character_Attack”. When I deleted “BB_Attack”, I forgot to update the reference and a (still to date) known issue (RunSubgraph (Static) node doesn’t refresh properly when the referenced subgraph asset is deleted.) made it so the “BB_Attack” graph still baked in the RunSubgraph node was not reset.
In regards to documentation, did you had a look at the “DemoInfo/Documentation.pdf”?
In addition to the graph structures, the demo provide example on how to couple Behavior Graph and C# system. For instance, it shows how to bind “B_StateMachine_Character” with the AgentController.cs and propagation of the graph to children component implementing the IStateMachineModifier interface.
So to sum up, there is (currently) no better way than to use a Blackboard asset with variables for all possible/expected subgraphs to be plugged into one RunSubgraph node as interface for that particular node AND if I want to be able to adjust any of those values via the BehaviorAgent inspector, I have to add them to the “outer” graph’s Blackboard and pass them through to the RunSubgraph node?
That sounds like a lot of clutter that could be avoided if it were possible to either create BlackboardVariable<Blackboard>s or be able to fill and set a Blackboard from Code (in which case the missing inspector access could be compensated with another MonoBehavior drawing an inspector, but instantiating, populating and passing Blackboards to a specific node from code isn’t currently possible, correct? Or manipulating instantiated (non-shared) blackboard variables of a standalone blackboard added to a graph’s blackboard?). Or at least have the variables of a Blackboard asset added to a graph’s blackboard be exposed in the inspector (so I wouldn’t have to “duplicate” variables from an “interface” blackboard to the outer graph’s blackboard)?
Can I officially request some better solution to that as a new feature?
Thanks for the clarifications on the demo. I did look at both the PDF and that class in particular (to see if it maybe contained a way to populate and assign blackboards as I wrote above). I didn’t spend much time with the PDF as I felt it mostly explains the “what”, not so much the “how” and “why”. I didn’t see anything I could transfer to my specific use case.
Despite my nagging I do like the package, was sad to hear about the personnel situation and appreciate your commitment to helping users with the package @MorganHoarauUnity!
We have plan to bring back the ability to assigned Blackboard Asset variables on the BehaviorGraphAgent inspector. This feature has been disabled because we need more work to handle variable with same names and types. But growing user feedback in that area will make us prioritize this work soon hopefully.
Once we reach that point, it will allow users to set default value of the blackboard asset variables that can be then assigned to variables field on the Run Subgraph.
I understand that this would only be one step toward what you are asking. We currently don’t investigate about adding support for BlackboardVariable<Blackboard> because it would add a new layer of complexity as each “runtime” blackboard would need to handle their own instance of underlying blackboard variable, and that adds a lot of complexity in several layers (initialization, shared BBV and runtime serialization and support…).
Your use case reminds me of this other thread where a user wanted to set several graphs with different blackboard set using a common blackboard asset. Sharing in case it can help and for future readers:
You can request new feature on the Product roadmap:
Good point, we will see how we can improve that in a future update.
I think I found a workaround for my particular case:
I created a custom action that takes a GameObject (Self) and string (Tag) and finds an (initially disabled) BehaviorGraphAgent on the GameObject that contains a string BBV called “Tag” with the same string as set on the custom action. In OnStart, I enable that BehaviorGraphAgent and disable it in OnEnd.
This way I can add any one of my subgraphs implementing for example movement behavior to a BehaviorGraphAgent on the same GameObject the “brain” graph runs on, adjust its parameters (without the need for an interface Blackboard containing parameters for every movement subgraph) and “tag” it “Movement” so the “brain” graph picks it up where I want the movement sub-behavior to happen. I do the same for other subgraphs and tag them with another context-specific string, so I only need to remember to set that same string in the custom action node and add it to every graph variant I want to be able to use that way (I could also replace the string tag with an enum later to eliminate the typo risk).