-
-
Notifications
You must be signed in to change notification settings - Fork 95
Svelto.ECS and GUI
It's simple to get confused when it's time to develop GUIs. GUIs are probably the most awkward part to develop in a game. Especially because game developers are often not web or desktop application developers, they may not know many of the vast amount of principles and theories behind the development of GUIs. For example, the Inversion of Control Container was initially designed, and makes much more sense to be used, for GUI based applications.
It would be a big mistake to think that libraries like NGUI, UnityGUI and so on are GUI frameworks. The are only libraries to render, but not manage, GUIs. There isn't much available for Unity in this sense, many frameworks are available on github and some examples are:
- https://github.com/AvaloniaUI/Avalonia (seems cool)
- https://github.com/chromealex/Unity3d.UI.Windows
- http://www.noesisengine.com/ (ugh super expensive, probably because it includes the renderer as well)
Knowing that MVVM is possibly the best pattern for GUI development, Svelto.ECS can get very close to it if used in the right way. Remember that the pattern alone is not enough to have a complete GUI framework, it would need also:
- data binding
- view serialization (like xaml)
- view states (same view with different states)
- view hierarchy (relationship between views)
- data injection
- a service layer
in Svelto.ECS this how you can map the single entities:
- the implementors are your views. In Unity, when implementors are monobehaviour, they hold your GUI elements
- the Entity Components are your models. They hold the transient data of your views
- The engines are your ModelView (Presenter), they hold the logic of the GUI
- DispatchOnSet and DispatchOnChange is used as databinding. Either the view or the presenter can change the same data through the entity component. Usually the engine (presenter) changes the data of the component (model) which through the implementor (view) actually set the value of the label. The implementor (view) receives the signal from the platform and delegate the logic to the engine (presenter) through the use of DispatchOnSet. DispatchOnSet and Change are due to a mega refactoring, which will make this process more comfortable. If you don't have an underlying platform and you don't use implementors (so only entity structs) then the patterns to use is the Model-View-Controller and the engine will become the Controller.
- if you have a Service Layer, the engines can trigger service request to load data from external data sources. The service request return the data through a DTO (Data Transfer Object), which is used to initialize or set the values of entity components (models).
- of course view state, view serialization and view hierarchy is up to you to manage at this moment.
Multiple EnginesRoot work quite well to encapsulate GUI entities and engines. Usually engines will communicate only through the service layer, so they don't need to communicate with other engines root. However if GUIs must communicate between each other, then engines could be put in communication through the use of injected observers. In future I will create a specific solution to let EnginesRoot communicate with each other. I know I often talk about "Service Layer", one day I will make my solution officially part of the Svelto Framework.
In my opinion it's very important to exploit the hierarchy nature of GUI elements/widgets. That's why a long time ago I wrote the Signal Chain Library. This can be still applied to Svelto.ECS, it's a notification system, similar to the one used in Flash, to let GUI elements communicate through the scene hierarchy, which in case of Unity happens through the gameobjects.
as part of the theory that the ECS design is a paradigm and that therefore everything can be implemented through ECS, GUIs should be implementable with this design too. While it's proved that MVP pattern matches perfectly the Implementor, Entity and Engine design, you may wonder what the point of using an engine is if there is always one entity to manage. In order to answer this question, it's necessary to think about modular design. Let's keep this simple for everyone: if you have the classic concept of widgets, like list boxes and any other more complex widget, you really would want to write the logic of these widgets once for all. This means that your GUIs that need to use a ListBox should create a ListBox entity; however since no custom data structure should be needed to be used, it would mean that every single listbox entry would be an entity on their turn. This reasoning applied everywhere would lead to the natural building of a GUI framework and reuse of the code, where engines will need to operate over several entities and not just one, according the number of Listbox currently built in this case. This is the theory, while the practice would probably still need more tools and something more akin to a GUI framework to make the job less onerous, but it's something to keep in mind to understand that ECS could be even more valid than the classic MVP pattern for the building of GUI frameworks.