Not a Bug

2.0.0a7 Class Events can be invoked in other classes only if both the broadcaster and the receiver classes are on the same object.

Ex-Crow 6 months ago updated 6 months ago 2

Alpha 6 docs say "class events can be invoked from any class, not just their parent class." 

This, however, is not true in alpha 7 - class events can only be invoked in other classes if both the broadcaster and the receiver classes are on the same object. Or if the event containing class is of Scene or Singleton scope. If you have two objects with two different Component classes on them, they can't communicate via public Class Events.

Test #1:

Two classes on two objects:
EventObject with ContainsEventClass: https://imgur.com/GCQIAUV
InvokeObject with InvokesEventClass: https://imgur.com/5pEh5mg

At Start event InvokeObject Invokes EVENT that is on EventObject. This does not work:

InvalidOperationException: Missing target.
Ludiq.Bolt.InvokeClassEvent.Invoke (Ludiq.Bolt.Flow flow) (at Assets/Ludiq/Bolt.Flow/Runtime/Units/Architecture/InvokeClassEvent.cs:105)
Ludiq.Bolt.Flow.InvokeDelegate (Ludiq.Bolt.ControlInput input) (at Assets/Ludiq/Bolt.Flow/Runtime/Flow.cs:513)
Rethrow as GraphPingException:
Ludiq.Bolt.Flow.InvokeDelegate (Ludiq.Bolt.ControlInput input) (at Assets/Ludiq/Bolt.Flow/Runtime/Flow.cs:517)
Ludiq.Bolt.Flow.Invoke (Ludiq.Bolt.ControlOutput output) (at Assets/Ludiq/Bolt.Flow/Runtime/Flow.cs:408)
Ludiq.Bolt.EventHandlerUnit`1[TArgs].Invoke (Ludiq.Bolt.GraphReference reference, TArgs args) (at Assets/Ludiq/Bolt.Flow/Runtime/Units/Events/EventHandlerUnit.cs:114)
Ludiq.Bolt.EventHandlerUnit`1+<>c__DisplayClass17_0[TArgs].b__0 (TArgs args) (at Assets/Ludiq/Bolt.Flow/Runtime/Units/Events/EventHandlerUnit.cs:73)
Ludiq.Bolt.EventBus.Invoke[TArgs] (Ludiq.Bolt.EventHook hook, TArgs args) (at Assets/Ludiq/Bolt.Core/Runtime/Events/EventBus.cs:153)
UnityEngine.Debug:LogException(Exception, Object)
Ludiq.Bolt.EventBus:Invoke(EventHook, EmptyEventArgs) (at Assets/Ludiq/Bolt.Core/Runtime/Events/EventBus.cs:157)
Ludiq.Bolt.ClassHost:InvokeEvent(EventHookID) (at Assets/Ludiq/Bolt.Core/Runtime/Architecture/ClassHost.cs:813)
Ludiq.Bolt.UnityClassHost:Start() (at Assets/Ludiq/Bolt.Core/Runtime/Architecture/UnityClassHost.cs:29)
Ludiq.Bolt.ClassComponent:Start() (at Assets/Ludiq/Bolt.Core/Runtime/Architecture/ClassComponent.cs:85)

Test #2 - Swap around Invoke and Event units:

Now Invoke is in ContainsEventClass: https://imgur.com/RhSFHn3

And Event unit is in InvokesEventClass: https://imgur.com/WwdXbGP

This also doesn't work:

NullReferenceException: Object reference not set to an instance of an object
Ludiq.Bolt.ClassEventUnit.OnStartListening (Ludiq.Bolt.GraphReference instance) (at Assets/Ludiq/Bolt.Flow/Runtime/Units/Architecture/ClassEventUnit.cs:207)
Ludiq.Bolt.ClassEventUnit.StartListening (Ludiq.Bolt.GraphReference instance) (at Assets/Ludiq/Bolt.Flow/Runtime/Units/Architecture/ClassEventUnit.cs:162)
Ludiq.Bolt.ClassEventUnit.OnActivating (Ludiq.Bolt.GraphReference instance) (at Assets/Ludiq/Bolt.Flow/Runtime/Units/Architecture/ClassEventUnit.cs:189)
Ludiq.Bolt.ClassEventUnit.Activate (Ludiq.Bolt.GraphReference instance) (at Assets/Ludiq/Bolt.Flow/Runtime/Units/Architecture/ClassEventUnit.cs:132)
Ludiq.Bolt.FlowBehaviour.Activate (Ludiq.Bolt.GraphReference instance) (at Assets/Ludiq/Bolt.Flow/Runtime/Graphs/FlowBehaviour.cs:47)
Ludiq.Bolt.ClassHost.Activate () (at Assets/Ludiq/Bolt.Core/Runtime/Architecture/ClassHost.cs:839)
Ludiq.Bolt.UnityClassHost.OnEnable () (at Assets/Ludiq/Bolt.Core/Runtime/Architecture/UnityClassHost.cs:17)
Ludiq.Bolt.ClassComponent.OnEnable () (at Assets/Ludiq/Bolt.Core/Runtime/Architecture/ClassComponent.cs:75)

Test #3: Both event class and Invoke class on the same object works:

Test #4: ContainsEventClass with Scene scope also works:

Bolt Version:
Unity Version:
Scripting Backend:
.NET Version (API Compatibility Level):
.NET 4.x
Bolt 2
Satisfaction mark by Ex-Crow 6 months ago
Not a Bug

Hi TowerCrow,

That is by design, let me explain more clearly than the changelog.

Events live on each instance on their parent class.

If your parent class is a Component class, then each component with that class will have its own separate event.

In those cases, it's important to pass the Target of the event in the first port of the Invoke / Listen units. By default, it is "self", which is why you noticed it working when both component classes are on the same game object.

If you want a global or scene-wide event without having to worry about specifying the targets manually, you should respectively use a Singleton or a Scene class as the parent class of your event. In those cases, the target port is no longer needed. That is indeed a very common use case, especially for the publisher-subscriber pattern.

The per component events are slightly more rare in gamedev, but useful nonetheless. For example, you could have a Door class with an OnOpen event. You may want other objects to subscribe to the open event of a specific door, not any door. That's when you would pass the door as the target parameter on the Listen unit, and leave it as default/self on the Invoke unit. 

Let me know if that wasn't clear or if you still think there is a bug despite that explanation!

> Let me know if that wasn't clear or if you still think there is a bug despite that explanation!

No bugs present as far as I can tell. I made the wrong assumptions about the system. Thank you for explaining!