Hi there, I’ve been looking at Behaviour package with specific regard to events. I appreciate you can send events to other behaviours using ‘Send Event Message’, but is there a way for:
- A Behaviour Graph to send a message to other gameObjects that do not use a behaviour graph (so just picked up in a monobehaviour)
- And vice versa, gameObjects without behaviour graphs, sending events via a monobehaviour to other gameobject’s behaviour graphs
Thanks for any pointers!
Hi @AntLewis
Thank you for your interest!
This is an area we are aware is missing documentation at the moment. We currently working with the documentation team to fill that gap.
In the meantime here are 2 different approaches you could use to achieve communication between Behavior Graphs and C# systems/components.
C# binding to a particular instance of BehaviorGraphAgent
You can get a reference to a BlackboardVariable from a BehaviorGraphAgent
using the BehaviorGraphReference
API.
You can then subscribed to the BlackboardVariable.OnValueChanged
event or in the case of an EventChannel, subscribe to BlackboardVariable.Value.Event
event.
Sample code
[SerializeField] private BehaviorGraphAgent m_Agent;
// Agent state has changed to [value] (where value is of type StateExample)
private BlackboardVariable<StateEventChannel> m_stateEventChannelBBV;
private BlackboardVariable<StateExample> m_stateBBV;
private void OnEnable()
{
if (m_Agent.BlackboardReference.GetVariable("StateEventChannel", out m_stateEventChannelBBV))
m_stateEventChannelBBV.Value.Event += OnStateEvent;
if (m_Agent.BlackboardReference.GetVariable("StateToReact", out m_stateBBV))
m_stateBBV.OnValueChanged += OnStateValueChanged;
}
private void OnDisable()
{
if (m_stateEventChannelBBV != null)
m_stateEventChannelBBV.OnValueChanged -= OnStateChanged;
if (m_stateBBV != null)
m_stateBBV.OnValueChanged -= OnStateValueChanged;
}
private void Update()
{
// your custom logic
// Send event to the event channel of the referenced agent.
// Only this instance of agent will receive it (except if the BlackboardVariable is 'Shared').
m_stateEventChannelBBV.Value.SendEventMessage(StateExample.Alert);
}
private void OnStateEvent(StateExample value)
{
// React to event
}
private void OnStateValueChanged()
{
// React to state change
}
C# binding using Event Channel asset
It is also possible to generate an instance of an Event Channel that can be use to received or emit message from. This is useful in case you want a global event channel that should be the same accross several graphs. You can then reference this asset in C# systems or component to react or send event message.
To do so, once you have created a new Event Channel from a Blackboard wizard:
- Go to the project view
- Right click > Create > Behavior > Event Channel >
%Name_of_Your_Event_Channel%
- This will create an instance of that Event Channel that can be assigned to Graph but also C# components.
- In C#, use the Event Channel API to listen or send new message.
Sample code
[SerializeField] private StateEventChannel m_EventChannel;
private void OnEnable()
{
m_EventChannel.Event += OnStateEvent;
}
private void OnDisable()
{
m_EventChannel.Event -= OnStateEvent;
}
private void Update()
{
// your custom logic
// Send event to the event channel instance.
// All graphs and C# systems on that same event channel instance will receive it.
m_EventChannel.SendEventMessage(StateExample.Alert);
}
private void OnStateEvent(StateExample value)
{
// React to event
}
I hope this will answer your question, and don’t hesitate to ask if you have any others
4 Likes
I really like how much you go into detail here. I am using a similar approach, to the first one you provided - I just joinked it from your behavior demo from the asset store. I have an Interface per EventChannel and assign all child objects from a bootstrapper that inherit from that Interface:
Example - IRequireHealthChangedEventChannel
Then loop through all child’s of the Bootstrapper Component which is on the same level as the BehaviorGraphAgent Component and call the: InitializeEventChannel(HealthChangedEventChannel healthChangedEventChannel);
Method from the Interface. In that way every Class has the correct reference to the behavior graph.
In my game I don’t have an Event Channel for each Attribute Type, but rather one Event Channel with multiple Variables. So EnergyChangedEventChannel, but multiple Variables in the Blackboard: HealthChangedEventChannel, StaminaChangedEventChannel, etc.
I then also only have one Interface: IRequireEnergyEventChannel. In my Bootstrapper, instead of looping through Children I have multiple Arrays, which are there for the different EnergyEventChannels. Example Health Channel: [SerializeField] MonoBehaviour[] healthRelatedChannels
- On Start I validate the MonoBehaviours as IRequireEnergyEventChannel
loop through them and assign the event channel ;)!
I find that solution pleasant.
1 Like
Thanks so much for such a great explanation and the example code, precisely what I was looking for!
1 Like