Can you confirm I'm using ECS 1.0 correctly?

The docs are all over the place, I just want to make sure I’m using OnUpdate correctly as it doesn’t make sense that I would iterate over all entities on every frame so I want to make sure this is the proper way.
should I even bother with healthDirty?

  1. Build query
  2. Create entity
  3. Use OnUpdate to iterate over entities added to the entity manager

Any suggestions or if I’m missing something please let me know.


public struct PlayerHealth : IComponentData
{
    public int value;
}

public struct NetworkIdentity : IComponentData
{
    public byte value;
}

public partial class HealthSystem : SystemBase
{
    EntityQuery query;
    EntityManager entityManager;
    bool healthDirty;

    public HealthSystem()
    {
        entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
        query = new EntityQueryBuilder(Allocator.Temp)
            .WithAllRW<PlayerHealth>()
            .WithAll<NetworkIdentity>()
            .Build(entityManager);
    }

    public void CreatePlayer(string name)
    {

        Entity playerEntity = entityManager.CreateEntity(
            typeof(PlayerHealth),
            typeof(NetworkIdentity)
        );

        entityManager.SetComponentData(playerEntity, new PlayerHealth { value = 100 });
        entityManager.SetComponentData(playerEntity, new NetworkIdentity { value = 1 });
    }

    protected override void OnUpdate()
    {
        if (healthDirty)
        {
            Entities.WithStoreEntityQueryInField(ref query)
                .WithEntityQueryOptions(EntityQueryOptions.FilterWriteGroup)
                .ForEach((ref PlayerHealth health, in NetworkIdentity id) =>
                {
                    // Do something with health
                }).ScheduleParallel();
        }
    }
}

I can spot quite a few issues and misunderstandings here, but that’s ok, learning curve is steep for ECS. These are:

  • Initialize SystemBase OnCreate.
  • SystemBase has a EntityManager accessible as a property already.
  • Things created with Allocator.Temp exist for a frame.
  • Avoid creating public fields or methods on SystemBase because these are not MonoBehaviours.
  • .WithChangeFilter() can do the job of your healthDirty. It makes Foreach code run only if other system accessed this component in Read-Write capacity. RW raises a possibility of value change and flags this component type as “dirty” internally (even if value did not change).
  • Entities.ForEach creates it’s own EntityQuery behind the scenes.
public partial class HealthSystem : SystemBase
{
    protected override void OnCreate ()
    {
        // initialize system here

        Entity playerEntity = EntityManager.CreateEntity(
            typeof(PlayerHealth),
            typeof(NetworkIdentity)
        );
        EntityManager.SetComponentData( playerEntity , new PlayerHealth { value = 100 });
        EntityManager.SetComponentData( playerEntity , new NetworkIdentity { value = 1 });
    }
    protected override void OnDestroy ()
    {
        // clean up system allocations here
    }
    protected override void OnUpdate()
    {
        Entities
            .WithChangeFilter<PlayerHealth>()
            .ForEach( ( ref PlayerHealth health , in NetworkIdentity id ) =>
            {
                // Do something with health
            })
            .ScheduleParallel();
    }
}

I should also tell you that this is just one way of iterating over entities and there are other ones. Some of these are more entry-level (Entities.ForEach included) while other are much verbose and technically demanding. Documentation page on this subject is here:

https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/systems-iterating-data-intro.html