DisclaimerThis post is some development of the ideas of the post
“Simple system of events in Unity” . I do not pretend to the only correct approach to the question and in general I am a mediocre programmer, regarding the mastodons who inhabit the habr. I am also very superficially familiar with C #, since I mostly use Java. However, fate brought me to Unity and I realized that I have some tool that can be repaid to the community for what I took from him. Simply put, make your own, albeit small, contribution to the open and, I want to believe, good code.
Those who are too lazy to read the problematics, conclusions, etc. can immediately see the code with examples on the github -
github.com/erlioniel/unity-smessageThere you can even download .unitypackage :)
ProblemJust starting to build a project in Unity, I immediately came to the conclusion that we need some kind of event system. Whatever religious fanatics tell you, but the situation when the GUI is tightly connected with game objects is the worst that can be. The architecture of the project, built on macaroni transfer of objects to each other, is very difficult to scale and is subject to change. Therefore, the event system should be.
')
Another question is that it is necessary here without fanaticism, because it’s not so long to reach a situation when the program’s behavior becomes impossible to track, because events are quite unpredictable abstractions. But what can we do to make the task a bit simpler?
Parameters - the first approachIf you look at the code of the article to which I refer initially, it is clear that the event system there is extremely simple, because the event can not contain any parameters. Initially, I implemented another system that allowed the use of parameters:
As you can see, the ENUM value was used as the event key, which somewhat simplified the work (you could always get a list of possible values from the IDE), and also pass some parameters without problems. This generally suited me for the first time.
Typification - the second approachThe main problem with the simple implementation of an event system is its poor predictability and the impossibility of using the IDE in writing code. At some point, I began to catch myself thinking that for any complex events I have to remember the order in which the arguments need to be passed so that they can be properly recorded in the model. And all these castes of a model to another in handlers too strained. In general, the expanded system began to behave unpredictably and required more and more attention and knowledge of the old code to maintain itself.
After one evening of witchcraft with generics, it turned out a system in which the IDE perfectly helps in any situation. The list of events is easy to get if you accept some rules for naming event classes (for example, the SMessage prefix ...), no castes, handlers immediately receive objects of the final class and all this is based on the classic C # delegates.
I suggest you analyze the work of the manager yourself, here are a couple of listings. Below you can find an example of use, which is much more interesting to the end user.
SManager.cs public class SManager { private readonly Dictionary<Type, object> _handlers;
SCallbackWrapper.cs internal class SCallbackWrapper<T> where T : AbstractSMessage { private SCallback<T> _delegates; public void Add(SCallback<T> value) { _delegates += value; } public void Remove(SCallback<T> value) { _delegates -= value; } public void Call(T message) { if (_delegates != null) { _delegates(message); } } }
Usage exampleExamples you can find on github -
github.com/erlioniel/unity-smessage/tree/master/Assets/Scripts/ExamplesBut here I will analyze the simplest case, how to use this system. For example, I will use the singleton implementation of the event manager, although you are free to create your instance for any needs. Suppose we need to create a new event that will notify that some object has been clicked. Create an event object.
The event model is the event marker itself, so you should create a new class for each event. Such is the fee for using IDE: <As a base class, AbstractSMessage is used, which can store an object in itself.
public class SMessageExample : AbstractSMessageValued<GameObject> { public SMessageExample (GameObject value) : base(value) { } }
In the object itself, we will need to trigger this event and pass the necessary arguments there.
public class ExampleObject : MonoBehaviour { public void OnMouseDown () { SManager.SCall(new SMessageExample(gameObject)); } }
And finally, let's create another object that will track this event.
public class ExampleHandlerObject : MonoBehaviour {
Everything is quite simple and obvious, but more importantly, the compiler / IDE will check everything for you and help you in your work.
PS I did not check the code, there may be errors :)
Instead of conclusionThe event system is a very powerful tool and you should not underestimate it. High code coherence is not as good as some programmers might think, especially when the project grows to a medium scale.
I hope the code will be useful to someone. I will be glad to some comments and suggestions.
UPD:Added abstract class AbstractSMessageValued and slightly updated the example in the article.