# 3. Entities Authoring, Baking and ISystem
Part of [[DOTS]]. Previous: [[2. Jobs and Burst for GameObject Projects]]. Next: [[4. Worked Example - Swarm Enemies]].
> [!note] Verification status
> These patterns match the Entities 6.5 manual examples (`Baker<T>`, `GetEntity(TransformUsageFlags.Dynamic)`, `ISystem`, `SystemAPI.Query<RefRW<>, RefRO<>>()`, `IJobEntity` must be `partial` and isn't Burst-compiled unless marked `[BurstCompile]`). **Code not compiled.**
## 3.1 Subscenes: where baking happens
1. In your scene: **right-click in the Hierarchy > New Sub Scene > Empty Scene**. Name it `GameplayEntities`.
2. GameObjects placed **inside the subscene** are **baked** into entities: in the editor when they change, and at build time.
3. GameObjects **outside** subscenes stay normal GameObjects (cameras, UI, audio, the player rig in a hybrid game).
## 3.2 A component and its authoring
```csharp
// Assets/Scripts/DOTS/Spin/Spin.cs
using Unity.Entities;
public struct Spin : IComponentData
{
public float RadiansPerSecond;
}
```
```csharp
// Assets/Scripts/DOTS/Spin/SpinAuthoring.cs
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
public class SpinAuthoring : MonoBehaviour
{
public float degreesPerSecond = 90f;
}
public class SpinBaker : Baker<SpinAuthoring>
{
public override void Bake(SpinAuthoring authoring)
{
// Dynamic: this entity moves at runtime, so it needs LocalTransform + LocalToWorld.
Entity entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent(entity, new Spin { RadiansPerSecond = math.radians(authoring.degreesPerSecond) });
}
}
```
**TransformUsageFlags** tell the baker which transform components you need:
- `None`: no transform (data-only entities such as a config singleton).
- `Renderable`: rendered but static.
- `Dynamic`: moved at runtime.
Asking only for what you use keeps chunks small. That's Data Locality again.
## 3.3 ISystem with SystemAPI.Query (main thread)
```csharp
// Assets/Scripts/DOTS/Spin/SpinSystem.cs
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
[BurstCompile]
public partial struct SpinSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<Spin>(); // don't run until at least one Spin exists
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
float dt = SystemAPI.Time.DeltaTime;
foreach (var (transform, spin) in SystemAPI.Query<RefRW<LocalTransform>, RefRO<Spin>>())
{
transform.ValueRW = transform.ValueRO.RotateY(spin.ValueRO.RadiansPerSecond * dt);
}
}
}
```
- `partial`: source generators fill in the plumbing.
- `RefRW` means read-write and `RefRO` means read-only. Declare intent honestly, because it drives job dependencies.
- No `OnDestroy` is needed here (the method has a default implementation in `ISystem`).
## 3.4 The same work as a parallel IJobEntity
```csharp
// Assets/Scripts/DOTS/Spin/SpinJobSystem.cs
using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
[BurstCompile]
public partial struct SpinJob : IJobEntity
{
public float DeltaTime;
// The parameters define the query: entities with LocalTransform (rw) and Spin (ro).
void Execute(ref LocalTransform transform, in Spin spin)
{
transform = transform.RotateY(spin.RadiansPerSecond * DeltaTime);
}
}
[BurstCompile]
public partial struct SpinJobSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state) => state.RequireForUpdate<Spin>();
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// Uses and updates state.Dependency automatically.
new SpinJob { DeltaTime = SystemAPI.Time.DeltaTime }.ScheduleParallel();
}
}
```
Use only **one** of the two Spin systems, or the cube spins twice as fast. Delete `SpinSystem.cs` once the job version works.
> [!tip] `[BurstCompile]` on the job
> `IJobEntity` isn't Burst-compiled by default. Put `[BurstCompile]` on the job struct as well as on the system methods.
## 3.5 Singletons: config data
```csharp
// Assets/Scripts/DOTS/Config/GameConfigAuthoring.cs
using Unity.Entities;
using UnityEngine;
public struct GameConfig : IComponentData
{
public float EnemySpeedMultiplier;
}
public class GameConfigAuthoring : MonoBehaviour
{
public float enemySpeedMultiplier = 1f;
}
public class GameConfigBaker : Baker<GameConfigAuthoring>
{
public override void Bake(GameConfigAuthoring authoring)
{
Entity e = GetEntity(TransformUsageFlags.None);
AddComponent(e, new GameConfig { EnemySpeedMultiplier = authoring.enemySpeedMultiplier });
}
}
```
In a system: `state.RequireForUpdate<GameConfig>()` in `OnCreate`, then `SystemAPI.GetSingleton<GameConfig>()` in `OnUpdate`. This is the ECS cousin of the ScriptableObject config in [[ScriptableObject Architecture]], and a tidier alternative to the **Service Locator** pattern.
## 3.6 Enableable components as state flags
```csharp
// Assets/Scripts/DOTS/States/Stunned.cs
using Unity.Entities;
public struct Stunned : IComponentData, IEnableableComponent
{
public float Remaining;
}
```
```csharp
// Assets/Scripts/DOTS/States/StunTimerSystem.cs
using Unity.Burst;
using Unity.Entities;
[BurstCompile]
public partial struct StunTimerSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
float dt = SystemAPI.Time.DeltaTime;
// Query only matches entities whose Stunned is ENABLED.
foreach (var (stun, stunEnabled) in
SystemAPI.Query<RefRW<Stunned>, EnabledRefRW<Stunned>>())
{
stun.ValueRW.Remaining -= dt;
if (stun.ValueRO.Remaining <= 0f)
stunEnabled.ValueRW = false; // no structural change
}
}
}
```
Bake `Stunned` onto enemies and then disable it immediately in the baker with `SetComponentEnabled<Stunned>(entity, false)`. A hit re-enables it and sets `Remaining`. Movement jobs then skip stunned enemies with `[WithDisabled(typeof(Stunned))]`, or with `WithNone<Stunned>()` in a `SystemAPI.Query` (which treats disabled as absent). This is an [[Enemy AI State Machine]] state, without archetype churn.
## Sources
- Baker overview: https://docs.unity3d.com/Packages/
[email protected]/manual/baking-baker-overview.html
- ISystem: https://docs.unity3d.com/Packages/
[email protected]/manual/systems-isystem.html
- SystemAPI.Query: https://docs.unity3d.com/Packages/
[email protected]/manual/systems-systemapi-query.html
- IJobEntity: https://docs.unity3d.com/Packages/
[email protected]/manual/iterating-data-ijobentity.html
- Enableable components: https://docs.unity3d.com/Packages/
[email protected]/manual/components-enableable.html
- Transform usage flags: https://docs.unity3d.com/Packages/
[email protected]/manual/transforms-usage-flags.html