Entity Component System for Unity: Getting Started
- Getting Started
- Stress Testing the Demo
- ECS: Performance by Default
- Removing Non-ECS Code
- Creating an Entity
- Adding Components
- MoveForward Component Data
- Movement System
- Making an Entity Prefab
- Spawning an Enemy Wave With NativeArray
- Activating the Player
- FacePlayer System
- Generating ComponentTags
- Destruction System
- More Systems and Cleanup
- Where to Go From Here
The cake is a lie.
For years, you’ve built your Unity applications around the best object-oriented practices: Classes, inheritance and encapsulation. But what if you’ve been doing it all wrong?
Unity’s new Data-Oriented Tech Stack, or DOTS, moves away from OOP toward data-oriented design. Central to this paradigm shift is the Entity Component System, or ECS.
ECS restructures your workflow around your game’s data and how it’s stored in memory. The result is more performant code that can handle massive scenes more efficiently.
Monobehaviour, we hardly knew ya.
In this tutorial, you’ll update part of a simple shoot ‘em up to use the Entity Component System. In doing so, you’ll learn the following:
- How to create Entities
- How to use hybrid ECS to ease into this new paradigm shift.
- Components, and how they can store data efficiently, if used correctly.
- Systems, the holders of logic and behaviors that act on your data, manipulating and transforming it for your game.
- How to hook up all of the above to fully leverage ECS.
Use the Download Materials button at the top or bottom of this tutorial to get the project files. Unzip and open the IntroToECSStarter project.
In Window ► PackageManager, install and update the following packages.
The following packages are essential for any ECS-based project:
- Entities implements the new ECS features.
- Hybrid Renderer renders all entities created in ECS.
The demo also uses the following packages for visuals:
- Cinemachine controls the follow camera.
- Universal Render Pipeline, or URP, holds the graphical settings.
- TextMeshPro displays the UI text elements.
Examine Assets/RW. Here, you’ll find some folders containing assets used to build the demo.
There’s a lot here, but in this tutorial you won’t touch the following directories:
There are folders for Scenes and Prefabs, but you’ll mostly work in the Scripts folder for this tutorial.
You’ll find several custom components for handling the player input, movement and shooting in Scripts/Player. Also, Scripts/Managers contains pre-built components to manage the game logic.
Stress Testing the Demo
Open SwarmDemoNonECS in Scenes.
Now, choose Maximize on Play in the Game view and set the Scale all the way to the left to get the full picture of the interface. Then enter Play mode to test the game.
Use the arrow or WASD keys to move the player’s tank. Point the mouse to aim the turret and left mouse button to fire bullets.
Notice that every few seconds, a new wave of enemies surrounds you. By default, the drones explode on contact, but don’t destroy the player.
This invincibility mode allows you to stress test your app. Hundreds of explosions, bullets and enemies clutter the Hierarchy.
To see the gameplay’s real-time impact, use the Stats in the Game window to track the main CPU time and the frame per second, or FPS count.
If you play long enough, you’ll notice the render time per frame increases while the FPS decreases. After a minute or so, the game slows down and becomes choppy as too many objects fill the screen.
Entity Component System to the rescue!
You can disable player invincibility for slightly more realistic test conditions. Locate EnemyDroneNonECS in RW/Prefabs/NonECS. Then open the prefab to edit and check Can Hit Player in the Enemy Non ECS component.
Save the prefab and play the game again. See your tank die when it collides with a drone.
Although enemies can’t keep spawning ad infinitum, Unity still stutters and spikes under too many objects at once.
ECS: Performance by Default
In classic Unity development, GameObjects and Monobehaviours let you mix data with behavior. For example, floats or strings can live side-by-side with methods like
Update. Making a mishmash of data types within one object translates into a memory layout like this:
For example, your GameObject might reference several data types like a Transform, Renderer and Collider. Unity scatters the varied data across non-contiguous memory. With enough objects, it spills into slower RAM.
In contrast, ECS tries to group similar data into chunks. It attempts to allocate memory with fewer gaps, packing the data more tightly. Doing this keeps as much as possible in the very fast CPU memory cache tiers (L1, L2, L3).
DOTS replaces object-oriented programming with data-oriented design. This architecture focuses on how to keep the data compact, which, unfortunately, means replacing the Monobehaviours you’re accustomed to using.
Instead, you’ll build your app from Entities, Components and Systems.
Entities are items that populate your program, although they are not objects in the traditional sense. An entity is a small integer ID pointing to other bits of data.
Components are the actual data containers. They are structs that hold values without any logic, and you’ll no doubt have a lot of them. ECS revolves around storing these small Components in a clever way.
Systems hold behaviors and logic. You’ll use them to manipulate and transform your data. Because Systems work on entire arrays of Entities at once, they can do so more efficiently.
Together these three parts form ECS.
Following this architectural pattern tends to cluster your data toward the very fast cache memory. The result is a significant speedup compared to the OOP equivalent. Unity calls this phenomenon performance by default.
Removing Non-ECS Code
Load SwarmDemoECS from Scenes, which removes the code used to generate the enemies.
Then open EnemySpawner.cs in Scripts/Managers.
Normally, this script would instantiate enemy waves, but some of the logic is missing. Notice the
SpawnWave methods are blank. You’ll fill those in during the tutorial.
Head on back to the Unity Editor. Disable DemoManagers and PlayerTank in the Hierarchy. But don’t delete them! You’ll need them later.
Confirm in Play mode that enemies no longer spawn. Don’t worry, you’ll add them back using ECS.
For now, you should have an empty scene except for a plane with a grid texture. This is a perfect blank slate to create some entities!