To date, a truly vast majority of both well-established companies and amateur game developers and indie teams use the Unity engine to develop games, applications, simulations, visualizations, etc. One of the reasons for such popularity is the rather low entry threshold compared to peers. It allows beginners to create their first game prototypes after just a few days or even hours of training.
Surely, such simplicity looks very tempting. Because indeed, it is enough to throw assets on the game scene, hang a couple of built-in components and a few scripts on them and all this will work. Unity will take care of almost everything on its own.
This approach brings results, but only until a certain point. Yes, this way you can create some quick and easy game prototypes that do not require the implementation of complex interactions between objects and the fulfillment of many conditions. But if you want to create a truly high-quality product, you will certainly run into the problem – such projects fail to scale up.
In case our ambitions go beyond a regular “clicker” implementation, the focus should be put on the problem of the game architecture extensibility. The more complex and diverse game mechanics, content variety, and possible interactions, the more difficult it is to perform correctly and avoid the so-called “spaghetti code”.
Connections between classes and modules become extremely tight. The interactions of classes are intertwined severely. Therefore, the changes to any of the existing mechanisms or adding a new one become impossible or incredibly difficult, since everything starts to depend on everything, and even worse – it becomes harder and harder to find those connections.
Making even the slightest change (which we thought should not break anything) in one part of the game brings chaos to completely unpredictable places. The attempts to add a new feature turns into a long way of establishing tight links with a large number of dependent modules and correcting a number of unforeseen errors. Of course, leveraging the principles and patterns of agile development in game creation will undoubtedly simplify the situation.
How Does ECS Work?
But there is another interesting approach that is very convenient to apply when developing games – the Entity-Component-System (ECS) pattern. In contrast to the standard approach, while implementing this pattern we are not dealing with specific objects whose behavior is described by the corresponding classes, but with “entities” that themselves do not carry any logic and are rather containers for “components”. The last ones are data warehouses that define the properties of entities with such components.
“Systems” are responsible for processing the entities and their components. These are the classes that most often handle entities with a similar set of components (and therefore similar properties). They are the central link in the whole logic.
It is noteworthy in this approach that for the system it does not matter what entities to work with. All of them will be processed according to clearly defined rules, no matter which type of game object represents them. For example, there is a system that moves all entities with the “Moveable” component. It doesn’t matter what exactly causes the movement – whether it’s a player or a projectile fired from a weapon – each one will be handled equally.
The entities may differ by the speed or, for example, the speed limit value inside the Moveable component (the data type and amount are determined in advance, so the initial values are set at the time the component is created).
We can have as many systems as we need and each of them will be responsible only for some particular game logic and will process only entities that correspond to given components filters. It allows us to achieve architecture consisting of highly separated modules.
What are the Benefits of ECS?
When you first start working with the ECS approach, everything seems too complicated. You literally need to rebuild the way of thinking and begin searching for similar object properties, grouping them into components, and creating systems to process them. Once done, you begin to see the game architecture differently.
Using ECS allows you to strictly separate logic from view. It also helps you to make logic incredibly flexible and customizable due to the lack of strong connections between entities. This way new features can be easily added or removed without fear of breaking existing ones.
Despite the fact that at the beginning of the game development much time needs to be spent on the ECS approach implementation, we will certainly win time in the middle and late phases of development due to the architecture flexibility. Which is several times better than the standard approach with only Unity MonoBehaviours.
Until recently, the most valuable advantages of ECS use in Unity were flexibility and potential for scaling up. Increased productivity was a less noticeable benefit.
However, the performance was improved by reducing the number of active MonoBehaviours. They have a lot of built-in functionality by default, therefore, the execution time for the system that processes a couple of hundred entities will be smaller compared to MonoBehaviours with each one having its own Update method.
We have successfully used one of the ECS solutions for Unity (Entitas) in a number of our games. So we’ve experienced ourselves how pleasant it is to refactor the code written using this approach. Nevertheless, none of the custom ECS solutions could squeeze more performance out of Unity than the engine itself allowed.
Is Unity DOTS the Next Step?
It all began to change when Unity realized the advantages and power of the ECS approach when developing games and began to create its own integrated ECS solution. This solution is part of a whole large data-oriented approach developed by Unity, called DOTS. It includes solutions for ECS (Entities), multi-threading (JobSystem), and a suite for compiling highly optimized native code (BurstCompiler).
The update is extremely global and is still under development. But we can feel its power by testing the demo versions of these packages. The main advantage is that since it is developed by Unity itself, it is already optimized at the engine level and allows you to squeeze the maximum performance out of the hardware.
Another distinguishing feature of Unity DOTS is that the components that we apply to entities should not be inherited from the class but from the struct, which is one of the factors for increasing productivity. This is due to the fact that structs are utilized by the level one memory processor cache, so the access time to this type of memory is minimal.
Additionally, DOTS allows you to structure data at a low level, which makes it possible to use processor pre-caching technology. This is especially noticeable when processing a large number of objects of the same type, especially if you use JobSystem multithreading, where the processing speed increases by tens to hundreds of times. If BurstCompiler is applied on top of this, then the productivity can grow by thousands of times.
Such numbers are simply unattainable using the standard Unity approach. There is one more benefit here – even if we use this approach to a game that ran at 60fps before optimization, the same game after optimization at the very same 60fps will consume fewer processor resources. It will increase battery life on mobile devices.
To Wrap It Up
Unfortunately, we cannot use DOTS as a substitute for all systems in the game yet. As it requires global changes in all other aspects of the game (physics, UI, animations, sounds) for their compatibility with this system. But Unity makes confident steps in this direction. Now we have the opportunity to use DOTS-compatible physics. Pretty soon DOTS-compatible animations will be released. Many other cool systems are also under development.
The opportunities Unity offers at the moment assure us that this movement is definitely in the right direction. It will allow developers to utilize the capabilities of the hardware to the maximum when developing games, applications, and simulations. And, of course, create a truly amazing experience for users.