ECS authoring and baking workflow
This section provides an example of the authoring and baking workflow in the Entity Component System (ECS).
The authoring and baking workflow in ECS connects the GameObject-based Editor experience with ECS high-performance runtime architecture. During authoring you can work with regular GameObjects and MonoBehaviour
components in the Unity Editor. The baking process transforms these GameObjects into optimized ECS entities and components. The workflow separates the content creation experience from runtime performance concerns.
Topics in this section are workflow steps that depend on previous steps to work. If you are following along in the Editor, follow the steps in order:
- Create the subscene for the example
- Create an entity from a GameObject
- Create a new ECS component
- Add a new component to an entity
- Create a system that rotates entities
Prerequisites
This workflow requires a Unity 6 project with the following packages installed:
Create the subscene for the example
The first step in the Entity Component System (ECS) workflow is to create a subscene. ECS uses subscenes instead of scenes to manage the content for your application, because Unity's core scene system is incompatible with ECS.
To create a subscene in Unity:
- In the Editor, open an existing scene.
- In the Hierarchy, right-click and select New Sub Scene > Empty Scene.
- In the prompt that appears, enter the name for the new subscene and save it. Unity adds the subscene to the open scene and you can now use it.
Create an entity from a GameObject
In this step you create an example GameObject and learn how the ECS framework converts GameObjects in a subscene into entities automatically. The process that ECS uses to convert GameObjects into entities is called baking.
To create an entity from a GameObject:
In the Hierarchy window, select a subscene.
Create a cube GameObject called Cube (menu path: GameObject > 3D Object > Cube).
ECS converts the new cube GameObject into an entity automatically.
When you create a GameObject in a subscene, ECS converts it into an entity automatically during the baking process.
Select the Cube GameObject. The Inspector window displays the Entity Baking Preview section. This section contains the entity components that ECS converted from the original GameObject.
Inspector window displaying the Entity Baking Preview section with Unity.Transforms.LocalToWorld selected.
For example, the Unity.Transforms.LocalToWorld component contains the transformation matrix that the rendering system uses to draw the entity. For the Cube entity, ECS creates this component using the information in the Transform component of the Cube GameObject. For more information, refer to Transforms in Entities.
Create a new ECS component
In ECS, components store entity data that systems can read or write.
In this example, an ECS component contains the rotation speed of a cube.
ECS has multiple component types, and this example uses the most common one, a component based on the IComponentData
interface:
Create a new C# script called
RotationSpeed.cs
and replace the contents of the file with the following code example.using Unity.Entities; // This component defines the rotation speed of an entity. public struct RotationSpeed : IComponentData { public float RadiansPerSecond; }
Now the project has a component that you can add to entities.
Unlike the GameObject workflow, you can't drag and drop an ECS component onto an entity.
In ECS there are two ways to add a component to an entity:
Using the baking process, where Unity adds an entity component when converting a GameObject into an entity.
Using the AddComponent API at runtime.
The next step demonstrates the former way of adding a component.
Add a new component to an entity
This step demonstrates how to add a component to an entity using the baking process. In the baking process, ECS takes a regular MonoBehaviour
component of a GameObject, and converts it into an entity component.
MonoBehaviour
components that you create for the purpose of converting data into entity components are called authoring components. The authoring and baking process provides a way to define and edit properties of GameObjects using the Editor interface and convert them into corresponding ECS runtime components.
For information on adding components to entities without using the authoring and baking process, refer to the section Add components to an entity.
Create an authoring component
An authoring component is a MonoBehaviour
component that provides a way to pass data from GameObject fields in the Editor into ECS components.
To create an authoring component:
Create a new C# script called
RotationSpeedAuthoring.cs
and replace the contents of the file with the following code.using Unity.Entities; using Unity.Mathematics; using UnityEngine; // The authoring component provides a way to define the rotation speed of // a GameObject in the Editor. ECS does not use the authoring component at // runtime, but converts it into an entity component using the Baker class. public class RotationSpeedAuthoring : MonoBehaviour { public float DegreesPerSecond = 360.0f; }
Add the script to the Cube GameObject.
Inspect a GameObject in Authoring and Runtime data modes
To understand the differences between GameObject and entity concepts better, inspect the cube GameObject in different data modes.
ECS provides two Inspector window data mode views: Authoring and Runtime. To switch between the modes, click the circle icon in the top-right corner of the Inspector window.
In the Authoring data mode, the Inspector window displays the Degrees Per Second property in the Rotation Speed Authoring component, as defined in the RotationSpeedAuthoring
class.
Click the data mode circle and select Runtime. The Inspector window displays the entity data view. The view does not contain the Degrees Per Second property, or the Rotation Speed component. This is because the component only exists on the GameObject and there is no converter or baker that would convert it into an entity component yet.
The next step is to create a baker class that converts the MonoBehaviour
component into an entity component.
Create the baker class
In ECS, the Baker class defines the conversion of GameObject data into entity data.
Add the baker class to the authoring component:
In the
RotationSpeedAuthoring.cs
script, replace the contents of the file with the following code, which adds theRotationSpeedBaker
class definition:using Unity.Entities; using Unity.Mathematics; using UnityEngine; // The authoring component provides a way to define the rotation speed of // a GameObject in the Editor. ECS does not use the authoring component at // runtime, but converts it into an entity component using the Baker class. public class RotationSpeedAuthoring : MonoBehaviour { public float DegreesPerSecond = 360.0f; } // In the baking process, this Baker runs once for every RotationSpeedAuthoring // instance in a subscene. class RotationSpeedBaker : Baker<RotationSpeedAuthoring> { public override void Bake(RotationSpeedAuthoring authoring) { // GetEntity returns an entity that ECS creates from the GameObject using // pre-built ECS baker methods. TransformUsageFlags.Dynamic instructs the // Bake method to add the Transforms.LocalTransform component to the entity. var entity = GetEntity(authoring, TransformUsageFlags.Dynamic); var rotationSpeed = new RotationSpeed { // The math class is from the Unity.Mathematics namespace. // Unity.Mathematics is optimized for Burst-compiled code. RadiansPerSecond = math.radians(authoring.DegreesPerSecond) }; AddComponent(entity, rotationSpeed); } }
This class has the following main functions:
The
Bake
method takes aRotationSpeedAuthoring
instance as an argument. This argument refers to the authoring component on a GameObject. This is the method where you define your custom logic for converting GameObject data into ECS data.The
GetEntity
method returns an entity that ECS creates from the GameObject using pre-built ECS baker methods.var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
The argument
TransformUsageFlags.Dynamic
instructs the Bake method to add the Transforms.LocalTransform component to the entity. This example uses the LocalTransform component on a further step to rotate the cube. For more information, refer to Transform usage flags.The following lines create a new instance of the Rotation Speed component, and the
AddComponent
method adds the component to the entity:var rotationSpeed = new RotationSpeed { RadiansPerSecond = math.radians(authoring.DegreesPerSecond) }; AddComponent(entity, rotationSpeed);
The new RotationSpeed
component is an ECS component, and it's using the data from the MonoBehaviour
authoring component.
Save the script and inspect the cube GameObject in the Runtime data mode. Now the cube has the Rotation Speed component with the Radians Per Second property.
The property name and units in the component are different from the MonoBehaviour
authoring component, where the name is Degrees Per Second and the units are degrees. This illustrates a common practice where you can add extra processing when converting data from GameObjects into entities.
Now you have the following setup:
A GameObject with the Degrees Per Second property in the Rotation Speed Authoring component, which you can edit from the Editor.
An entity with the Radians Per Second property in the Rotation Speed component, which the ECS framework updates dynamically based on the changes on the GameObject.
The next step demonstrates how to create a system that rotates the cube entity.
Create a system that rotates entities
This task describes how to create an ECS system that rotates entities.
Unlike the GameObject workflow, in ECS you don't assign scripts directly to specific entities. Instead, a system queries for entities that match certain criteria in a world, and then performs actions on all of them.
Create the rotation system
To create a rotation system:
Create a new C# script called
RotationSystem.cs
and replace the contents of the file with the following code:using Unity.Burst; using Unity.Entities; using Unity.Transforms; // This example defines an unmanaged system based on the ISystem interface. // ECS uses code generation, which is why the struct must be declared as partial. public partial struct RotationSystem : ISystem { // The BurstCompile attribute indicates that the method should be compiled // with the Burst compiler into highly-optimized native CPU code. [BurstCompile] public void OnUpdate(ref SystemState state) { // In ECS, use the DeltaTime property from Entities.SystemAPI.Time. float deltaTime = SystemAPI.Time.DeltaTime; // Create a query that selects all entities that have a LocalTransform // component and a RotationSpeed component. // In each loop iteration, the transform variable is assigned // a read-write reference to LocalTransform, and the speed variable is // assigned a read-only reference to the RotationSpeed component. foreach (var (transform, speed) in SystemAPI.Query<RefRW<LocalTransform>, RefRO<RotationSpeed>>()) { // ValueRW and ValueRO both return a reference to the actual component // value. The difference is that ValueRW does a safety check for // read-write access while ValueRO does a safety check for read-only // access. transform.ValueRW = transform.ValueRO.RotateY( speed.ValueRO.RadiansPerSecond * deltaTime); } } }
Enter Play mode. The cube should spin in both the Game view and the Scene view.
When you enter Play mode, ECS automatically instantiates systems based on ISystem
interface.
To affect entities, the system implements a query in the OnUpdate
method. In each frame the system selects all entities that have both the LocalTransform and the RotationSpeed components, and executes the code defined in the foreach
loop on them.
The following code in the system rotates entities around the Y axis:
transform.ValueRW = transform.ValueRO.RotateY(speed.ValueRO.RadiansPerSecond * deltaTime);
ValueRW
and ValueRO
methods are special ECS methods that return a reference to the component and conduct a safety check for read-write (ValueRW
) or read-only (ValueRO
) access.
For more information about the transform operations, refer to the section Transform operations.
The [BurstCompile]
attribute indicates that the method should be compiled with the Burst compiler into highly-optimized native CPU code. For more information, refer to the Burst compiler documentation.
To see the query functionality in action:
Create several more cube GameObjects in the subscene.
In the Authoring data mode, add the Rotation Speed Authoring component to some, but not all cubes.
On cubes that have the Rotation Speed Authoring component, change the Degrees Per Second values.
Enter Play mode.
Only the cubes that have the Rotation Speed Authoring component rotate. Cubes rotate at a speed defined in the Degrees Per Second property.