A collection of questions after diving into ECS!

  1. If you using the monobehavior technique to create your entities (ComponentDataWrapper) instead of directly creating entities, I cannot figure out how to initialize the entity. I tried overriding awake on the ComponentDataWrapper but that broke things. I can set simple things right in the editor, but i want to make a Unity.Mathematics.Random which needs to be initialized with a seed.

  2. Systems seem to have 2 ways to run(correct me here if i’m wrong). One technique is unity main thread safe

protected override void OnUpdate()
  {
    using (NativeArray<Entity> spawnersList = spawners.ToEntityArray(Allocator.TempJob))
    {
      foreach (Entity spawnerEntity in spawnersList)
      {
        UnitSpawner spawner = EntityManager.GetComponentData<UnitSpawner>(spawnerEntity);
        spawner.timer += Time.deltaTime;
        if (spawner.timer >= spawner.frequency)
        {
          spawner.timer -= spawner.frequency;
          GameObject prefab = EntityManager.GetSharedComponentData<UnitSpawnerShared>(spawnerEntity).prefab;
          Entity entity = EntityManager.Instantiate(prefab);

          Position position = EntityManager.GetComponentData<Position>(spawnerEntity);

          float2 offset = spawner.rand.NextFloat2Direction();
          offset *= spawner.rand.NextFloat(0, spawner.radius);
          position.Value += new float3(offset.x, 0, offset.y);
          EntityManager.SetComponentData(entity, position);

          //EntityManager.DestroyEntity(spawner);
        }
        EntityManager.SetComponentData(spawnerEntity, spawner);
      }
    }
  }

the other style is to schedule a job

public float3 startingDirection;
  private void OnValidate()
  {
    if (!EditorApplication.isPlaying)
    {
      UnitMovement um = Value;
      um.direction = math.normalizesafe(startingDirection);
      Value = um;
    }
  }

Should you always use a job if you can? Is the reason the first example is not using a job because its referencing a gameobject?

Thanks!

  1. Use constructors, attributes (RuntimeInitializeOnLoadMethod for example), initialize in systems etc.
  2. All systems runs ALWAYS on main thread, only difference is dependency management.
  1. eizenhorn is correct you would use a file that loads into the scene where you create all your entities. Something like:
var entityManager = World.Active.GetOrCreateManager<EntityManager>();

        for (int i = 0; i < 3; i++)
        {
            Entity playerUnit = entityManager.CreateEntity(PlayerUnitArchetype);
            entityManager.SetComponentData(playerUnit, new Position { Value = new float3(i * 5, 0.5f, 0) });
            entityManager.AddSharedComponentData(playerUnit, cubeRenderer);
        }
  1. This might be a little incorrect. ComponentSystems are indeed called from the main thread but jobs leverages multi-thread through the use of workers:
    Unity - Manual: Job system overview

To answer OP, yes you should definitely use jobs when you can and regular systems when you can’t. Unity just came out with a new sample that shows what you might be trying to achieve:

https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/Samples/Assets/HelloECS/HelloSpawnACube/HelloSpawnerSystem.cs

In the example they grab the HelloSpawner component (which stores the prefabs) and instantiate through the EntityManager. So if you had 5 spawners in the scene with 5 different prefabs, it’ll go iterate through all of them and spawn your entities.

The system itself is still always executed on the main thread.

And there is nothing stopping you creating jobs in a regular ComponentSystem that will run in separate threads (and you don’t even need to finish them before the system returns though this idea is extremely unwise.)

1 Like

Thanks for the input. However the first question was not answered as I was hoping. The suggestions were do not use the unity based entity creation. But that is what I am trying to do(ComponentDataWrapper). Using constructor is not possible because unity is creating the entity for me. That is why I cannot figure out how to customize it.

This is because it seems like you no longer need the bootstrapping file if you want to create all your entities via game objects to move more towards traditional scene design.

you define an “InitializeRandom” component and attach to your GOE
you write a system that takes the “InitializeRandom” and your rng holder components, initializes the rng from some global entropy pool (e.g. a single System.Random or UnityEngine.Random) and removes the Init component

eg:

struct Rng : IComponentData {public Unity.Mathematics.Random value;}

struct InitializeRng : IComponentData {} // empty

class RngComponent : ComponentDataWrapper<InitializeRng>{}

class RandomInitSystem : ComponentSystem {
    System.Random globalRandom;
    OnCreateManager() {globalRandom = new Random();}
    OnUpdate() {
        ForEach((Entity e, ref InitializeRng _) {
            RemoveComponent<InitializeRng>(e);
            AddComponent(e, new Rng{value = new Random(globalRandom.Next()});
        });
    }
}

Thanks! This is prob what I was missing. I will have to look at how to ensure this executes before the other one.

Im having trouble parsing the foreach loop in your on update. All the examples I have seen so far show a syntax to my original post. You make a component group and then in your update you use something like

class RandomInitSystem : ComponentSystem
{
  private ComponentGroup randoms;

  protected override void OnCreateManager()
  {
    randoms = GetComponentGroup(typeof(InitializeRandom));
  }

  protected override void OnUpdate()
  {
    using (NativeArray<Entity> randomsList = randoms.ToEntityArray(Allocator.TempJob))
    {
      foreach (Entity randomEntity in randomsList)
      {
        EntityManager.RemoveComponent<InitializeRandom>(randomEntity);
        EntityManager.AddComponent(randomEntity, typeof(RandomNumbers));
        EntityManager.SetComponentData(randomEntity, new RandomNumbers() { rand = new Unity.Mathematics.Random((uint)UnityEngine.Random.Range(0, 999999)) });
      }
    }
  }
}

I don’t know if this will help but I usually initialize the entity array as EntityArray

EntityArrayiterator = randoms.GetEntityArray();
if(iterator.Length == 0) { return; }
NativeArray<Entity> Entities = new NativeArray<Entity>(
  iterator.Length, Allocator.Temp, NativeArrayUnitiliazedMemory
);
iterator.CopyTo(Entities);

for(int i=0;i<Entities.Length; ++i)
{
  //Whatever you need to do
}
Entities.Dispose()

I find it easier to iterated through like this