Designed to be easy to use, extendable and optimized where it's possible.
An EventBus is a mechanism that allows different components to communicate with each other without knowing about each other. A component can send an Event to the EventBus without knowing who will pick it up or how many others will pick it up.
- interface based
- listeners priority sorting
- ability to send functions and messages
- local buses creation with the ability to subscribe as a listener
- timeline compatibility
- filtered messaging
- expandability
// subscriber class definition
public class SampleListener : Subscriber,
IListener<GlobalEvent>, // react on GlobalEvent(Enum) messages
IListener<IEvent<GlobalEvent>>, // react on IEvent<GlobalEvent> messages
IDamageTaker,
IHandle<IDamageTaker> // provide IDamageTaker interface for invokation
// send enum event to the GlobalBus
GlobalBus.Send(GlobalEvent.Activate);
// send IEvent<GlobalEvent> with custom data
GlobalBus.SendEvent(GlobalEvent.Activate, 1f);
// send action to the GlobalBus
GlobalBus.SendAction<IDamageTaker>(damageTaker => damageTaker.TakeDamage(1));
// send with filtration
GlobalBus.Send(GlobalEvent.Activate, sub => sub is Unit);
// none MonoBehaviour subscriber
public class SampleListenerClass : ISubscriberOptions, // optional priority and name options
IListener<GlobalEvent>,
IDamageTaker,
IHandle<IDamageTaker>
{
public void Subscribe() => GlobalBus.Subscribe(this);
public void UnSubscribe() => GlobalBus.UnSubscribe(this);
Through Unity Package Manager git URL: https://github.com/NullTale/UnityEventBus.git
Or copy-paste somewhere inside your project Assets folder.
On OnEnable event Listener will connect to the desired bus and will start receiving messages from it.
using UnityEngine;
using UnityEventBus;
// same as SimpleListener : Subscriber, IListener<string>
public class SimpleListener : Listener<string>
{
public override void React(in string e)
{
Debug.Log(e);
}
}
In the unity editor, you can set up the behavior in detail. Such as subscription targets and priority.
Note: Lower priority triggers first, highest last, equal invokes in order of subscription
To create your custom listener you need to implement at least one IListener<>
interface and subscribe to the desired bus.
Note: Listener can have any number of IListener<> interfaces.
using UnityEngine;
using UnityEventBus;
public class SimpleListener : MonoBehaviour, IListener<string>
{
private void Start()
{
// somewhere in code...
// send string event to the global bus
GlobalBus.Send<string>("String event");
}
// subscribe to the global bus
private void OnEnable()
{
GlobalBus.Subscribe(this);
}
// unsubscribe from the global bus
private void OnDisable()
{
GlobalBus.UnSubscribe(this);
}
// react to an event
public void React(in string e)
{
Debug.Log(e);
}
}
You can also implement ISubscriberOptions
interface to setup debug name and listener priority.
public class SimpleListener : MonoBehaviour, IListener<string>, ISubscriberOptions
{
public string Name => name;
public int Priority => 1;
To create a local bus, you need to derive it from the EventBusBase
class
Note: Event bus can be subscribed to the other buses like a listener, using Subscribe and UnSubscribe methods.
using UnityEngine;
using UnityEventBus;
public class Unit : EventBusBase
{
private void Start()
{
// send event to this
this.Send("String event");
}
}
You can also derive it from the EventBus
class to configure auto-subscription and priority.
Note: If EventBus implements any Subscribers interfaces, they will be automatically subscribed to it.
public class Unit : EventBus
Sometimes it can be more convenient to look at listeners as a set of interfaces, the SendAction
method extension is used for this. For an object to be an action target it must execute an interface IHandle<>
with the interface type it provides.
public class Unit : EventBus
{
[ContextMenu("Heal Action")]
public void Heal()
{
// send invoke heal action on the IUnitHP interface
this.SendAction<IUnitHP>(hp => hp.Heal(1));
}
}
public interface IUnitHP
{
void Heal(int val);
}
// implement IHandle<IUnitHP> interface to be an action target
public class UnitHP : Subscriber,
IUnitHP,
IHandle<IUnitHP>
{
public int HP = 2;
public void Heal(int val)
{
HP += val;
}
}
Event is a message that contains keys and optional data. To send an Event, the SendEvent
extension method is used. To receive events must be implemented IListener<IEvent<TEventKey>>
interface, where TEventKey is a key of events, which listener wants to react.
using UnityEngine;
using UnityEventBus;
// unit derived from the EventBus class, he is receives events and propagate them to subscribers
public class Unit : EventBusBase
{
private void Start()
{
// send creation event without data
this.SendEvent(UnitEvent.Created);
}
[ContextMenu("Damage")]
public void DamageSelf()
{
// send event with int data
this.SendEvent(UnitEvent.Damage, 1);
}
[ContextMenu("Heal")]
public void HealSelf()
{
this.SendEvent(UnitEvent.Heal, 1);
}
}
// unit event keys
public enum UnitEvent
{
Created,
Damage,
Heal
}
using UnityEngine;
using UnityEventBus;
// OnEnable will subscribe to the unit and start to listen messages
// same as UnitHP : EventListener<UnitEvent>
public class UnitHP : Subscriber,
IListener<IEvent<UnitEvent>>
{
public int HP = 2;
// reacts to UnitEvents
public override void React(in IEvent<UnitEvent> e)
{
switch (e.Key)
{
// event with damage or heal key always containts int data
case UnitEvent.Damage:
HP -= e.GetData<int>();
break;
case UnitEvent.Heal:
HP += e.GetData<int>();
break;
case UnitEvent.Created:
break;
}
}
}
Also multiple data can be sent through an event.
// send multiple data
SendEvent(UnitEvent.Created, 1, 0.2f, this);
// get multiple data
var (n, f, unit) = e.GetData<int, float, Unit>();
// or
if (e.TryGetData(out int n, out float f, out Unit unit))
{
// do something with data
}
The small extension that allow you to use Timeline.SignalAsset
as messages in order to.
React to signals.
Send signals from the director,
or through script or MonoBehaviour.
Request is needed to get permission for something from another subscriber. Request works just like Event, contains key and optional data, but it can be either approved or ignored and he will propagate until first approval. It is in fact a usable event with the only difference that you can get the result of execution. The SendRequest
method extension is used to send a Request.
public class Unit : EventBus
{
[ContextMenu("HealRequest ")]
public void HealRequest()
{
if (this.SendRequest(UnitEvent.Heal, 1))
{
// request was approved
// do something, play animation or implement logic
}
}
}
public class UnitHP : Subscriber,
IListener<IRequest<UnitEvent>>
{
public int HPMax = 2;
public int HP = 2;
public void React(in IRequest<UnitEvent> e)
{
switch (e.Key)
{
case UnitEvent.Heal:
{
// if can heal, approve heal request
var heal = e.GetData<int>();
if (heal > 0 && HP < HPMax)
{
HP += heal;
e.Approve();
}
} break;
}
}
}