Skip to content

01 — Hello Entity

A spinning cube. Introduces components, tags, templates, systems, and world setup.

Source: com.trecs.core/Samples~/Tutorials/01_HelloEntity/

What it does

A single entity holds a Rotation component. A fixed-update system advances the rotation each tick; a presentation system applies it to a Unity Transform.

Schema

A schema is the components, tags, and templates that describe your entities:

public static class SampleTags
{
    public struct Spinner : ITag { }
}

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

Rotation comes from Common/ and is reused across tutorials.

See Components, Tags, and Templates.

Systems

SpinnerSystem (fixed update)

Spins anything with 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));
    }
}

MatchByComponents = true iterates every entity with a Rotation component, regardless of tags. See Queries & Iteration.

SpinnerGameObjectUpdater (presentation)

Syncs the simulation rotation onto a Unity Transform. The sample wires a single transform in by constructor; later samples introduce the RenderableGameObjectManager pattern for per-entity GameObjects:

[ExecuteIn(SystemPhase.Presentation)]
public partial class SpinnerGameObjectUpdater : ISystem
{
    readonly Transform _spinnerCube;

    public SpinnerGameObjectUpdater(Transform spinnerCube) => _spinnerCube = spinnerCube;

    [ForEachEntity(MatchByComponents = true)]
    void Execute(in Rotation rotation)
    {
        _spinnerCube.rotation = rotation.Value;
    }
}

[ExecuteIn(SystemPhase.Presentation)] runs at the variable (display) frame rate rather than the fixed timestep — the right place for anything touching Unity GameObjects. See Systems and Accessor Roles.

Wiring it up

The composition root builds the world, registers the systems, and hands lifecycle callbacks back to Bootstrap:

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

world.AddSystems(new ISystem[]
{
    new SpinnerSystem(RotationSpeed),
    new SpinnerGameObjectUpdater(spinnerCubeTransform),
});

A separate scene initializer creates the entity once during the init phase. The accessor is created up front with AccessorRole.Unrestricted, since init code lives outside any system:

public class SceneInitializer
{
    readonly WorldAccessor _world;

    public SceneInitializer(World world)
    {
        _world = world.CreateAccessor(AccessorRole.Unrestricted);
    }

    public void Initialize()
    {
        _world.AddEntity<SampleTags.Spinner>();
    }
}

See World Setup and Entities.

Concepts introduced

  • Components — unmanaged structs implementing IEntityComponent
  • Tags — empty structs implementing ITag
  • Templates — declare an entity's components and tags
  • SystemsISystem + [ForEachEntity] for iteration
  • MatchByComponents — iterate by component presence instead of tags
  • [ExecuteIn(SystemPhase.Presentation)] — run at the display frame rate