Great video, as always. While I think I see the benefits of using Zenject signals, in that the other classes won't need to know anything about other clases, I think it may have some drawbacks. From your example I would say that Installers would become way too bloated with the signals, and the code to execute for each one. Those are just some of my thoughts, I think it would just be cleaner by using C# events or UniRx observables. The responsibilities, chain of executions and dependencies would look much clearer, at least for me.
I think the solution to bloated Installers is to have a separate Installer for each signal, or set of signals based on feature. For instance you could have a UIInstaller that handles all signals related to the UI and a PlayerInstaller that could handle signals fired by the player, etc.
From Zenject docs: Note also that while the result will be more loosely coupled, this isn't always going to be better. Signals can be misused just like any programming pattern, so you have to consider each case for whether it's a good candidate for them or not.
It's powerful, but the lambada trick kinda rubs me the wrong way. Feels like having a god script that's just executing methods all across your project. Would be cool to see more of the other way, in which the objects fire, subscribe, and unsubscribe from the message, and the installer just binds the signal bus. This way it's up to the individual class to decide if it cares that a message went off somewhere, and what to do about it (although this obviously can make it annoying to debug if the dev makes a complex web of them).
I'm using ScriptableObject-based event and variable classes (i.e. containers of a singular event or variable) to the same end (well, partially). :) Nicely decouples the components from each other, since they all just reference a shared ScriptableObject instance. I even made writable and readonly versions of these ScriptableObject-derived classes (so you can associate a writable instance to a component as read only, if the given component should only read it), and the variable classes expose an event that notifies subscribers (if any) of the value changes (very similar to the INotifyPropertyChanged stuff in .Net). One nice thing I find about this is that I can write totally generic MonoBehaviour components that rely only on these inputs. For example one component I wrote modulates sound pitch based on a ScriptableObject-based float input (for e.g. speed-based engine sound), another skins UI elements based on a color input, a third one replaces the Button UI component with one that invokes my events, plus I wrote generic triggers for animations, particle systems, etc. Now I'm implementing sort of a composite pattern in my event system, creating a component that acts as an aggregate event trigger which fires if all (or alternatively any) events defined in it do. These aggregate/composite triggers can be even nested into each other, since they all act as a singular trigger; although that will get messy pretty fast. :D But I do reference concrete classes in my design, unfortunately (mostly using the singleton pattern, which is of course still awfully common in Unity dev), so Zenject is still pretty relevant.
Hey Charles! So when I hear this my first thought is reactive properties with UniRx... specifically for something like health or with a UI attached solely to it. The second is just creating an observable and having other classes subscribe. Can you help me better understand the differences here? The big one I see is that for a class to subscribe to an observable or Rx property they need to be aware of the class creating it - whereas Zenject will handle that awareness aspect when using signals. I hope I'm using the right words & this is making enough sense lol. Still figuring things out. So when I think about when to use either implementation, it seems to me signals would always be superior at least theoretically, besides in cases where the listener is inherently coupled with the observed class/value - like for a health bar for a single enemy. So what I'm wondering is how would you go about deciding between the two when you want to do something?
Yes! Reactive Properties in UniRx can fill the same role. They really are just another flavor of the pattern. That's the thing with design patterns, though. They offer a set of high level guidelines, but they can have many specific implementations. As for the differences, well Signals are Zenject's flavor and Reactive Properties are UniRx's flavor. If you'd like to stick to functional programming, then I'd go with UniRx. Zenject Signals, on the other hand, are really handy if you're already doing DI with Zenject. On the most basic level, you can accomplish this exact code using C#'s build in events and delegates. All of these frameworks are merely tools to abstract the design pattern a little differently.
Now convention has changed am i right? In documentation we see something like signalbus, and when i create the class i cant just simply inherit "Signal", it doesnt work. Maybe i am doing something wrong.
Hey there Charles! Great work with your videos. I'm a big fan! I was wondering about the performance of Zenject signals. How taxing is it? Is having totally decoupled systems that only interact with signals a good idea?
I haven't done any heavy profiling, but from my experience they are very performant. Having a totally decoupled system would really depend on the scope of your game. I won't write it off as a bad idea, but the mental overhead might be a little high when everything is 100% decoupled.
What would be the benefit of using Zenject Signal instead of other EventManager systems like assetstore.unity.com/packages/tools/integration/event-system-dispatcher-12715 ? To me, those other systems are more elegant for an Event-based architecture.
I appreciate the videos a lot, I hope you managed to get rid of typos in the future ones, they quite numerous in this series. Great work anyway thanks!
I can't really see why one would use that. It is so much cleaner, easier and faster to just use system.events and system.actions and just hook one class directly to other... The methos you described also add unnecessary complexity to a simple thing, that is hooking class events..
I like your Programming Tutorial Video. when i have time, will go "liked" 1 by 1. RUclips is big, but rarely see good tutorial video. I spend money on udemy, Packpub, for PAID tutorial, but end up disappointed.
📦 Download Zenject (now Extenject): assetstore.unity.com/packages/tools/utilities/extenject-dependency-injection-framework-157735?aid=1100l3e8M&pubref=extenject-zenject-series-ep8-pinned-comment
❤️ Consider supporting this channel by becoming a Patron: www.patreon.com/infalliblecode
This video is much better than previous videos from the series. Real example makes it more interesting and easy to understand.
Time to update this one. SignalBus has been out for a while now. :)
Great video, as always.
While I think I see the benefits of using Zenject signals, in that the other classes won't need to know anything about other clases, I think it may have some drawbacks. From your example I would say that Installers would become way too bloated with the signals, and the code to execute for each one.
Those are just some of my thoughts, I think it would just be cleaner by using C# events or UniRx observables. The responsibilities, chain of executions and dependencies would look much clearer, at least for me.
I think the solution to bloated Installers is to have a separate Installer for each signal, or set of signals based on feature. For instance you could have a UIInstaller that handles all signals related to the UI and a PlayerInstaller that could handle signals fired by the player, etc.
From Zenject docs:
Note also that while the result will be more loosely coupled, this isn't always going to be better. Signals can be misused just like any programming pattern, so you have to consider each case for whether it's a good candidate for them or not.
It's powerful, but the lambada trick kinda rubs me the wrong way.
Feels like having a god script that's just executing methods all across your project.
Would be cool to see more of the other way, in which the objects fire, subscribe, and unsubscribe from the message, and the installer just binds the signal bus. This way it's up to the individual class to decide if it cares that a message went off somewhere, and what to do about it (although this obviously can make it annoying to debug if the dev makes a complex web of them).
I'm using ScriptableObject-based event and variable classes (i.e. containers of a singular event or variable) to the same end (well, partially). :) Nicely decouples the components from each other, since they all just reference a shared ScriptableObject instance. I even made writable and readonly versions of these ScriptableObject-derived classes (so you can associate a writable instance to a component as read only, if the given component should only read it), and the variable classes expose an event that notifies subscribers (if any) of the value changes (very similar to the INotifyPropertyChanged stuff in .Net). One nice thing I find about this is that I can write totally generic MonoBehaviour components that rely only on these inputs. For example one component I wrote modulates sound pitch based on a ScriptableObject-based float input (for e.g. speed-based engine sound), another skins UI elements based on a color input, a third one replaces the Button UI component with one that invokes my events, plus I wrote generic triggers for animations, particle systems, etc. Now I'm implementing sort of a composite pattern in my event system, creating a component that acts as an aggregate event trigger which fires if all (or alternatively any) events defined in it do. These aggregate/composite triggers can be even nested into each other, since they all act as a singular trigger; although that will get messy pretty fast. :D
But I do reference concrete classes in my design, unfortunately (mostly using the singleton pattern, which is of course still awfully common in Unity dev), so Zenject is still pretty relevant.
It's Amazing, thank you Infallible Code!
I love your channel!
best quality videos and content!!!
you're a genius!
I really appreciate that!
Hey Charles! So when I hear this my first thought is reactive properties with UniRx... specifically for something like health or with a UI attached solely to it. The second is just creating an observable and having other classes subscribe. Can you help me better understand the differences here? The big one I see is that for a class to subscribe to an observable or Rx property they need to be aware of the class creating it - whereas Zenject will handle that awareness aspect when using signals. I hope I'm using the right words & this is making enough sense lol. Still figuring things out.
So when I think about when to use either implementation, it seems to me signals would always be superior at least theoretically, besides in cases where the listener is inherently coupled with the observed class/value - like for a health bar for a single enemy. So what I'm wondering is how would you go about deciding between the two when you want to do something?
Yes! Reactive Properties in UniRx can fill the same role. They really are just another flavor of the pattern. That's the thing with design patterns, though. They offer a set of high level guidelines, but they can have many specific implementations.
As for the differences, well Signals are Zenject's flavor and Reactive Properties are UniRx's flavor. If you'd like to stick to functional programming, then I'd go with UniRx. Zenject Signals, on the other hand, are really handy if you're already doing DI with Zenject.
On the most basic level, you can accomplish this exact code using C#'s build in events and delegates. All of these frameworks are merely tools to abstract the design pattern a little differently.
Now convention has changed am i right? In documentation we see something like signalbus, and when i create the class i cant just simply inherit "Signal", it doesnt work. Maybe i am doing something wrong.
Hey there Charles! Great work with your videos. I'm a big fan! I was wondering about the performance of Zenject signals. How taxing is it? Is having totally decoupled systems that only interact with signals a good idea?
I haven't done any heavy profiling, but from my experience they are very performant. Having a totally decoupled system would really depend on the scope of your game. I won't write it off as a bad idea, but the mental overhead might be a little high when everything is 100% decoupled.
Hello! Please make a video about the Factory and the implementation of various features, as well as the pool) Many will be grateful to you for this.
hmm is interesting but i prefer to stick with the default C# events and delegates, no need to reinvent the wheel.
Hi! Could you tell what plugins for VS are you using for Unity / coding?
I use Resharper and VsVim
Oh... Resharper. Thanks! VsVim is fantastic :D
Yeah I can’t live without vim! Lately I’ve been using Rider instead of VS, and I’ve really been loving it.
Isn't that about to be the same as C# default event? And also, how to unload if its necessary, for example, if I open another window?
What would be the benefit of using Zenject Signal instead of other EventManager systems like assetstore.unity.com/packages/tools/integration/event-system-dispatcher-12715 ? To me, those other systems are more elegant for an Event-based architecture.
Usage changed. See usage in the documentation here github.com/svermeulen/Zenject/blob/master/Documentation/Signals.md
I appreciate the videos a lot, I hope you managed to get rid of typos in the future ones, they quite numerous in this series. Great work anyway thanks!
How would one go about using both Zenject and UniRX at the same time?
having the signal handling code in the installer seems wrong to me.
I can't really see why one would use that. It is so much cleaner, easier and faster to just use system.events and system.actions and just hook one class directly to other... The methos you described also add unnecessary complexity to a simple thing, that is hooking class events..
The important difference is that Zenject can inject dependencies into the signal handlers.
My brain exploded once more. I’m not able to follow anything
I like your Programming Tutorial Video. when i have time, will go "liked" 1 by 1. RUclips is big, but rarely see good tutorial video. I spend money on udemy, Packpub, for PAID tutorial, but end up disappointed.
Yeah that's cool and stuff, but it is just a parody of Publish Subscribe Pattern
This is outdated now.
There is fun in reverse-engineering the code especially if it's outdated...