I'm using a very similar approach to Method 1 in my current solo project. The main difference is that instead of using nested classes for organisation, I use structs instead and make the Actions static. I also use a ResetAllEvents method that I can call on exiting play mode in case I've forgotten to unsubscribe anywhere:
public static class Events { public struct Game { public static Action GameStarted; public static Action GameEnded; public static Action GamePaused; public static Action GameUnpaused; public static Action HighScoreChecked; public static Action LivesChanged; public static Action ComboBanked; public static Action ComboMultiplierChanged; public static Action ScoreChanged; public static Action DifficultyChanged; public static Action ActiveTargetsEmptied; public static Action ApplicationQuit; }
public struct Target { public static Action BombExploded; public static Action CrateBroken; } public struct UI { public static Action ScreenClosed; public static Action ViewClosed; public static Action MainMenuViewRequested; public static Action DifficultySelectViewRequested; public static Action SettingsViewRequested; public static Action HighScoreViewRequested; public static Action NameEntryViewRequested;
public static Action VolumeDisplaySliderChanged; // View -> Presenter : Slider has been changed public static Action NameEntered; }
public struct Audio { public static Action SoundTriggered; public static Action PositionalSFXTriggered;
public static Action AudioChannelSoundTriggered; } public struct Settings { public static Action ChannelVolumeLoaded; // Model -> View : Mixer level has been loaded }
public static void ResetAllEvents() { var eventFields = typeof(Events).GetNestedTypes(BindingFlags.Public | BindingFlags.Static) .SelectMany(type => type.GetFields(BindingFlags.Public | BindingFlags.Static)) .Where(field => typeof(Delegate).IsAssignableFrom(field.FieldType)); foreach (var field in eventFields) { field.SetValue(null, null); } } }
Vary independent and simple method to organize interactions in code! Thank you a lot! P.S. Also, like for Moonlight Sonata. I had flashbacks from Earthworm Jim 2 xd
For the third method, is there a way to use regular C# event or delegate instead of UnityEvent for the "HealthEvent"? I am asking because I have heard that UnityEvent is slower than regular c# event and that the main benefit of UnityEvent over a regular c# event is that it can be used in the Unity inspector. Since, in this scenario, we are not (at least I don't think we are) using UnityEvent in the Unity inspector, should we still use UnityEvent over regular c# event? Or is there a difference between a UnityEvent and a regular c# event that makes it so that only a UnityEvent can work in this scenario?
I used UnityEvents because you can say use the new keyword. It’s easier to explain. I don’t find recent benchmarks speaking against UnityEvents vs native C# event. So this is another reason I didn’t bother much. If you want to switch to native C# Actions. Then the creation of new Action is done with this API: Delegate.CreateDelegate(…). It’s worth trying :D
UnityEvents is about 10 times slower than C# events, but you dont have to worry about that, because the actual time that It consumes is so little that you Will never notice the difference, unless you have thousands of listener multiplied by thousands of events.
Wouldn't it be problematic if: imagine we have 50 enemies, each one of them have basic events (walk, attack, die animations, sounds to play at each anim, health bars etc.), if one of the enemies attacks and sends event via manager, all of them, so 50 enemies have to check if the event is from them, anims, sounds, UI checks it (with channel approach too, cuz 50 prefabs listens the same channel). Within a fight that would escalate very quickly 5 enemies attacks, 5 dies, and other 40 is walking, that is horrendous amount
With lots of enemies you might have other issues first, like playing 50 sound effects at once will already create a huge mess, you might need to have a dedicated sound manager that prioritises what sound to play. Enemies are also a good candidate to go to unity DOTS + ECS :D
I love the first solution and it's held up in a fairly large project. I came back because I thought I broke something - I just forgot to end an event call with .Invoke in one monobehavior. Duh-doy.
Thank you! I mean how i can subribe an event from code. I have figured it out "EventMangerGeneric.Player.OnPlayerHealthChanged.Get().AddListener" and then RemoveListener... @@this-is-gamedev
I'm using a very similar approach to Method 1 in my current solo project. The main difference is that instead of using nested classes for organisation, I use structs instead and make the Actions static. I also use a ResetAllEvents method that I can call on exiting play mode in case I've forgotten to unsubscribe anywhere:
public static class Events
{
public struct Game
{
public static Action GameStarted;
public static Action GameEnded;
public static Action GamePaused;
public static Action GameUnpaused;
public static Action HighScoreChecked;
public static Action LivesChanged;
public static Action ComboBanked;
public static Action ComboMultiplierChanged;
public static Action ScoreChanged;
public static Action DifficultyChanged;
public static Action ActiveTargetsEmptied;
public static Action ApplicationQuit;
}
public struct Target
{
public static Action BombExploded;
public static Action CrateBroken;
}
public struct UI
{
public static Action ScreenClosed;
public static Action ViewClosed;
public static Action MainMenuViewRequested;
public static Action DifficultySelectViewRequested;
public static Action SettingsViewRequested;
public static Action HighScoreViewRequested;
public static Action NameEntryViewRequested;
public static Action VolumeDisplaySliderChanged; // View -> Presenter : Slider has been changed
public static Action NameEntered;
}
public struct Audio
{
public static Action SoundTriggered;
public static Action PositionalSFXTriggered;
public static Action AudioChannelSoundTriggered;
}
public struct Settings
{
public static Action ChannelVolumeLoaded; // Model -> View : Mixer level has been loaded
}
public static void ResetAllEvents()
{
var eventFields = typeof(Events).GetNestedTypes(BindingFlags.Public | BindingFlags.Static)
.SelectMany(type => type.GetFields(BindingFlags.Public | BindingFlags.Static))
.Where(field => typeof(Delegate).IsAssignableFrom(field.FieldType));
foreach (var field in eventFields)
{
field.SetValue(null, null);
}
}
}
That’s also great!
Simply fantastic. It changed the way I normally worked !
Awesome!
Nice video and project.
After some Problems it works.
Great Job
Anything inside eventManager should be marked as static
@@this-is-gamedev Yes, i found it. I love it.
Just amazing work! Fantastic
this is a cool approach!
Well done man, thank you!
Vary independent and simple method to organize interactions in code!
Thank you a lot!
P.S.
Also, like for Moonlight Sonata. I had flashbacks from Earthworm Jim 2 xd
A little classical music is much better than some random beats :D
This is awesome!
Very well explained, thank you :)
Thanks! 🙏
Question. is this the same as
using System;
public Action OnAction;
are they same? I get confused with event sytem may i need more practice.
public static readonly PlayerEvents Player = new PlayerEvents(); PlayerEvents cannot be found... Should it be PlayerEvent?
Will first simple method work for a game like Valheim for instance?
Yes, works for all games. The type of game is quite irrelevant actually.
For the third method, is there a way to use regular C# event or delegate instead of UnityEvent for the "HealthEvent"? I am asking because I have heard that UnityEvent is slower than regular c# event and that the main benefit of UnityEvent over a regular c# event is that it can be used in the Unity inspector. Since, in this scenario, we are not (at least I don't think we are) using UnityEvent in the Unity inspector, should we still use UnityEvent over regular c# event? Or is there a difference between a UnityEvent and a regular c# event that makes it so that only a UnityEvent can work in this scenario?
I used UnityEvents because you can say use the new keyword. It’s easier to explain.
I don’t find recent benchmarks speaking against UnityEvents vs native C# event. So this is another reason I didn’t bother much.
If you want to switch to native C# Actions. Then the creation of new Action is done with this API: Delegate.CreateDelegate(…).
It’s worth trying :D
@@this-is-gamedev Thank you very much for the response. I will try to look into that API, thank you for the recommendation.
UnityEvents is about 10 times slower than C# events, but you dont have to worry about that, because the actual time that It consumes is so little that you Will never notice the difference, unless you have thousands of listener multiplied by thousands of events.
Wouldn't it be problematic if: imagine we have 50 enemies, each one of them have basic events (walk, attack, die animations, sounds to play at each anim, health bars etc.), if one of the enemies attacks and sends event via manager, all of them, so 50 enemies have to check if the event is from them, anims, sounds, UI checks it (with channel approach too, cuz 50 prefabs listens the same channel). Within a fight that would escalate very quickly 5 enemies attacks, 5 dies, and other 40 is walking, that is horrendous amount
With lots of enemies you might have other issues first, like playing 50 sound effects at once will already create a huge mess, you might need to have a dedicated sound manager that prioritises what sound to play.
Enemies are also a good candidate to go to unity DOTS + ECS :D
This sounds cool, but this is also much harder to understand =')
I love the first solution and it's held up in a fairly large project. I came back because I thought I broke something - I just forgot to end an event call with .Invoke in one monobehavior. Duh-doy.
How i can subribe an event in the UI?
The UI can publish an event (that you define) after a certain action like OnClick and another script listens to that event
Thank you! I mean how i can subribe an event from code. I have figured it out "EventMangerGeneric.Player.OnPlayerHealthChanged.Get().AddListener" and then RemoveListener... @@this-is-gamedev
let's goooooo)))
or just get messagepipe