Skip to content

01 — Hello Entity

The simplest Trecs sample — a spinning cube. Introduces the fundamental building blocks: components, tags, templates, systems, and world setup.

Source: Samples/01_HelloEntity/

What It Does

A cube rotates continuously around the Y axis. The rotation speed is configured when the system is created.

Schema

Components

[Unwrap]
public partial struct Rotation : IEntityComponent
{
    public quaternion Value;
}

The GameObjectId component (from Common) maps the entity to a Unity GameObject.

Tags

public struct Spinner : ITag { }

Template

public partial class SpinnerEntity : ITemplate, IHasTags<SampleTags.Spinner>
{
    public Rotation Rotation = new(quaternion.identity);
    public GameObjectId GameObjectId;
}

Systems

SpinnerSystem (Fixed Update)

Rotates all entities that have a Rotation component:

public partial class SpinnerSystem : ISystem
{
    readonly float _rotationSpeed;

    public SpinnerSystem(float rotationSpeed)
    {
        _rotationSpeed = rotationSpeed;
    }

    [ForEachEntity(MatchByComponents = true)]
    void Execute(ref Rotation rotation)
    {
        float angle = World.DeltaTime * _rotationSpeed;
        rotation.Value = math.mul(rotation.Value, quaternion.RotateY(angle));
    }
}

Uses MatchByComponents = true to iterate all entities with Rotation, regardless of tags.

SpinnerGameObjectUpdater (Variable Update)

Syncs the ECS rotation to the Unity transform:

[VariableUpdate]
public partial class SpinnerGameObjectUpdater : ISystem
{
    readonly GameObjectRegistry _gameObjectRegistry;

    public SpinnerGameObjectUpdater(GameObjectRegistry gameObjectRegistry)
    {
        _gameObjectRegistry = gameObjectRegistry;
    }

    [ForEachEntity(MatchByComponents = true)]
    void Execute(in GameObjectId id, in Rotation rotation)
    {
        var go = _gameObjectRegistry.Resolve(id);
        go.transform.rotation = rotation.Value;
    }
}

Marked [VariableUpdate] because it touches Unity GameObjects — rendering should happen at the display frame rate, not the fixed timestep. Though in this case it doesn't matter since rotation is updated in fixed and we aren't using interpolation in this sample.

World Setup

var world = new WorldBuilder()
    .AddEntityType(SampleTemplates.SpinnerEntity.Template)
    .Build();

world.AddSystems(new ISystem[]
{
    new SpinnerSystem(rotationSpeed: 2f),
    new SpinnerGameObjectUpdater(gameObjectRegistry),
});

// Initialize is called separately (via the initializables list)
// Entity creation happens in SceneInitializer.Initialize:
var world = world.CreateAccessor();

world.AddEntity<SampleTags.Spinner>()
    .Set(gameObjectRegistry.Register(cube.gameObject));

Concepts Introduced

  • Components are unmanaged structs implementing IEntityComponent
  • Tags are empty structs implementing ITag that classify entities
  • Templates declare which components and tags an entity has
  • Systems implement ISystem and use [ForEachEntity] for iteration
  • [VariableUpdate] separates rendering from simulation
  • MatchByComponents iterates by component presence instead of tags