Unity DOTS: Our top 5 features

Game production, Technology

Objective

In this article we share our experiences with Unity's Data Oriented Technology Stack (DOTS) while working on our DOTS project “Recursive Reckoning”. We also show some simple code examples from our project to give a first insight into the newly released features of Unity.

Recursive Reckoning

Gameplay
The idea of Recursive Reckoning was to create a tower defense game with a top-down perspective fixed in the middle, potentially offering infinite zoom across multiple levels, although we have currently limited the zoom to 5 levels. The player can also rotate the camera 360° and tilt it downwards up to 45°.

Due to the infinity aspect, it is not suitable to build each level ten times the size of the previous one. This approach would very quickly reach the limits of the game engine. A better option is to separate the visual representation from the simulation by building all levels with a scale of 1 next to each other in the world. Then the visualizations are moved and resized to fit their presentation layer. For example, the first level is displayed with a scale of 0.1, the second with a scale of 1 and the third with 10 times its simulated scale. In this way, the player can see up to 3 levels at the same time.

To allow the player to zoom out seamlessly, the displayed levels are swapped to show the next three levels. In the previous example, layers 0, 1 and 2 would be replaced by layers 1, 2 and 3. And of course, the camera position would be scaled down by a factor of 10 to provide a seamless experience. To hide the layer change perfectly, timing is crucial. Everything has to happen in the exact same frame. Thanks to the efficiency of DOTS, there is no noticeable drop in performance.

What is ECS?

Because we use Unity's Entity Component System (ECS), the more than doubled amount of objects (entities) for the visual copies is not a big problem. ECS is very efficient when it comes to modifying the data of many entities. It breaks a game down into entities, components, and systems. Entities are nothing more than identifiers or containers that represent individual game objects. Components, on the other hand, contain the data and behavior associated with specific aspects of an entity, such as position, speed, or health. Finally, systems are responsible for processing and manipulating entities based on their components. The principles of ECS revolve around data-oriented design and separation of concerns. By coherently storing similar data in memory, ECS allows for efficient data processing, as the CPU can work with larger blocks of data simultaneously. This approach results in fewer cache misses and reduces latency in memory access. A key difference between ECS and object-oriented programming (OOP) lies in their fundamental design philosophy. OOP focuses on encapsulating behavior within objects, while ECS prioritizes data and divides behavior into systems. This separation allows for better performance optimization, scalability, and parallelism in game development. In implementation, ECS offers significant advantages over the traditional MonoBehaviour approach. By using ECS, it is possible to achieve better performance due to reduced memory fragmentation and improved cache utilization. Additionally, ECS facilitates parallel processing, enabling efficient use of multi-core CPUs. However, implementing ECS requires a shift in mindset and a different approach to organizing and managing game logic compared to the familiar MonoBehaviour-based implementation.

Are you ready to bring your game vision to life? Contact us today to learn more about how we can help you successfully bring your ideas to life with our game development services!

Our top 5 DOTS aspects.

In the past few months, we have been working on a game project based on Unity's Data Oriented Technology Stack. The goal of the project was to experiment with and learn about DOTS, and perhaps even create a fun little game out of it. We mainly focused on the Entities Package (ECS), but we also utilized the Job System and the Burst Compiler wherever possible. Here are our top 5 aspects that we enjoyed the most while working with DOTS, based on our tower defense project 'Recursive Reckoning'.

1. Performance

It's not necessary to optimize every line of code to achieve a significant performance boost compared to mono-scripting. If the core principles of the ECS concept are followed, there won't be many performance issues in most games.

The ECS architecture organizes similar data in memory as contiguous blocks, allowing the CPU to easily access and process them as a whole without many memory jumps.

Here are two interesting videos that compare the performance of MonoBehaviour and DOTS in scripts:

Unity DOTS: Comparing performance and physics: How many Rigidbodies can Unity support ? [2020 DOTS Edition]

2. Forced clean code

Unity DOTS encourages developers to apply clean and efficient programming practices. Unlike traditional approaches, where developers may rely on high-level abstractions, DOTS forces them to think about how they design and implement their systems. The data-oriented mindset promotes a more explicit and organized code structure, leading to improved readability and maintainability.

By breaking down game logic into smaller, reusable components, a clear separation of concerns can be ensured. This modularity enables better code understanding and facilitates the identification and resolution of emerging issues. Additionally, DOTS prevents unnecessary object allocations and promotes the use of fixed-size arrays, reducing memory fragmentation and improving overall performance.

3. Modularity

Due to the ECS architecture, game mechanics will be highly reusable for other projects. For example, the health mechanic we created for our DOTS project, Recursive Reckoning, can easily be copied into a new project and is almost plug-and-play: it only requires attaching the authoring script to an entity, and it will have a health value that can take damage.

The health module consists of the following parts:

  • Health Component: Contains float variables for the current and maximum health of an entity.
  • Damage Buffer: A list of damage values from different sources, which are summed up at the end of each frame and subtracted from the current health.
  • Health Authoring: A MonoBehaviour script that can be attached to a GameObject in the inspector window of the editor, which is then converted into an entity with health components via its baker class.
  • Health Aspect: Provides easy access to a set of components, which is technically not necessary but offers a clean way of accessing components.
  • Apply Damage System: Schedules the "Apply Damage" job to run in parallel at the end of each frame for each entity that has the components of the health aspect.
  • Apply Damage Job: Sums up all the damage buffer values and subtracts the result from the current health. It uses the health aspect to access the required components.
  • Destroy On No Health System: Schedules the "Destroy On No Health" job to run in parallel every frame for each entity that has the components of the health aspect.
  • Destroy On No Health Job: Marks any entity with 0 health for destruction, using the health aspect to access the health.
4. Hybrid approach

A major concern regarding DOTS is the fear that it will eventually replace the old Mono-scripting backend. However, the opposite is true. Unity's current plans for DOTS involve expanding the toolset available in the editor. It is already possible to use a hybrid approach, combining the strengths of both worlds.

By using a SystemBase class as an interface, it is possible to receive inputs from our user interface and input system, and, for example, create a new tower entity at the cursor position when the player presses the left mouse button.

The hybrid approach also uses authoring and baker scripts to convert GameObjects within subscenes into entities.

Here is an example of an authoring MonoBehaviour with its baker class:

public class HealthAuthoring : MonoBehaviour {
public float value;
}
public class HealthBaker : Baker {
public override void Bake(HealthAuthoring authoring) {
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent(entity, new HealthComponent {Value = authoring.value, Max = authoring.value});
AddBuffer(entity);
}
}
5. Entity jobs

As one of the cornerstones of DOTS, the job system is also designed for efficiency, both in processing and programming. Among other things, it enables easy parallelization of tasks, making optimal use of modern multicore CPUs.

A special type of job is the Entity Job. These jobs process data for many entities efficiently. It is simply necessary to create a function that should be executed once for each entity and queue that function as a job for execution. The job system handles dependencies and execution order to avoid common issues in parallel programming, such as race conditions or deadlocks.

Here is a small example of a job:

[BurstCompile]
public partial struct DestroyOnNoHealthJob : IJobEntity {
public EntityCommandBuffer.ParallelWriter Ecb;

private void Execute([EntityIndexInQuery] int sortKey, HealthAspect health) {
if (!health.isReadyToDie) return;
Ecb.DestroyEntity(sortKey, health.Entity);
}
}

And this is how the job is scheduled for execution:

[BurstCompile]
public partial struct DestroyOnNoHealthSystem : ISystem {
[BurstCompile]
public void OnCreate(ref SystemState state) {
state.RequireForUpdate();
}

[BurstCompile]
public void OnUpdate(ref SystemState state) {
var ecbSingleton = SystemAPI.GetSingleton();

new DestroyOnNoHealthJob {
Ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged).AsParallelWriter(),
}.ScheduleParallel();
}

Usefull tools

Unity has created some debugging tools for the editor to assist with DOTS development. They are available under Window > Entities. The "System" window, in particular, has been very helpful, as it shows the execution order of all predefined and custom systems. Additionally, the inspector has been upgraded to allow debugging of entities and their attached components.

Personal conclusion

All in all, I am very satisfied with the Data-Oriented Technology Stack, which was released in version 1.0 this year. I see myself playing around with DOTS, Unity Physics, and all the new features in the future, just because I enjoyed it this time. This technology offers great potential, and I am excited to see what will come next as it gains more attention and more developers take the opportunity to leverage the powerful tools it offers.

Questions & Wishes

We hope you like our article and would like to invite you to share your thoughts and questions on the topic with us. If you have any questions, comments or feedback on the content of this article, please don't hesitate to let us know in the comments section. We're always happy to hear from our readers and engage in meaningful discussions about game development.
Just ask us anything you want to know and we'll do our best to provide the answers you're looking for. Thank you for your support and we look forward to hearing from you!

Comment form

Write comment

* These fields are required

Comments

Comments

No Comments

More exciting articles

Unity UIToolkit

Unity modernizes the user interfaces and functionalities for developers. UIToolkit is intended to be a simplified solution for UI element creation and…