Search Unity

A collection of questions after diving into ECS!

Discussion in 'Entity Component System' started by greyhoundgames, Jan 24, 2019.

  1. greyhoundgames

    greyhoundgames

    Joined:
    Jan 24, 2014
    Posts:
    32
    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
    Code (CSharp):
    1. protected override void OnUpdate()
    2.   {
    3.     using (NativeArray<Entity> spawnersList = spawners.ToEntityArray(Allocator.TempJob))
    4.     {
    5.       foreach (Entity spawnerEntity in spawnersList)
    6.       {
    7.         UnitSpawner spawner = EntityManager.GetComponentData<UnitSpawner>(spawnerEntity);
    8.         spawner.timer += Time.deltaTime;
    9.         if (spawner.timer >= spawner.frequency)
    10.         {
    11.           spawner.timer -= spawner.frequency;
    12.           GameObject prefab = EntityManager.GetSharedComponentData<UnitSpawnerShared>(spawnerEntity).prefab;
    13.           Entity entity = EntityManager.Instantiate(prefab);
    14.  
    15.           Position position = EntityManager.GetComponentData<Position>(spawnerEntity);
    16.  
    17.           float2 offset = spawner.rand.NextFloat2Direction();
    18.           offset *= spawner.rand.NextFloat(0, spawner.radius);
    19.           position.Value += new float3(offset.x, 0, offset.y);
    20.           EntityManager.SetComponentData(entity, position);
    21.  
    22.           //EntityManager.DestroyEntity(spawner);
    23.         }
    24.         EntityManager.SetComponentData(spawnerEntity, spawner);
    25.       }
    26.     }
    27.   }
    the other style is to schedule a job
    Code (CSharp):
    1. public float3 startingDirection;
    2.   private void OnValidate()
    3.   {
    4.     if (!EditorApplication.isPlaying)
    5.     {
    6.       UnitMovement um = Value;
    7.       um.direction = math.normalizesafe(startingDirection);
    8.       Value = um;
    9.     }
    10.   }
    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!
     
  2. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,684
    1. Use constructors, attributes (RuntimeInitializeOnLoadMethod for example), initialize in systems etc.
    2. All systems runs ALWAYS on main thread, only difference is dependency management.
     
  3. SKCode

    SKCode

    Joined:
    Mar 3, 2017
    Posts:
    20
    1. eizenhorn is correct you would use a file that loads into the scene where you create all your entities. Something like:


    Code (CSharp):
    1. var entityManager = World.Active.GetOrCreateManager<EntityManager>();
    2.  
    3.         for (int i = 0; i < 3; i++)
    4.         {
    5.             Entity playerUnit = entityManager.CreateEntity(PlayerUnitArchetype);
    6.             entityManager.SetComponentData(playerUnit, new Position { Value = new float3(i * 5, 0.5f, 0) });
    7.             entityManager.AddSharedComponentData(playerUnit, cubeRenderer);
    8.         }

    2. This might be a little incorrect. ComponentSystems are indeed called from the main thread but jobs leverages multi-thread through the use of workers:
    https://docs.unity3d.com/Manual/JobSystemOverview.html


    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-Technologi...elloECS/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.
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    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.)
     
    GilCat likes this.
  5. greyhoundgames

    greyhoundgames

    Joined:
    Jan 24, 2014
    Posts:
    32
    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.
     
  6. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    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:
    Code (CSharp):
    1. struct Rng : IComponentData {public Unity.Mathematics.Random value;}
    2.  
    3. struct InitializeRng : IComponentData {} // empty
    4.  
    5. class RngComponent : ComponentDataWrapper<InitializeRng>{}
    6.  
    7. class RandomInitSystem : ComponentSystem {
    8.     System.Random globalRandom;
    9.     OnCreateManager() {globalRandom = new Random();}
    10.     OnUpdate() {
    11.         ForEach((Entity e, ref InitializeRng _) {
    12.             RemoveComponent<InitializeRng>(e);
    13.             AddComponent(e, new Rng{value = new Random(globalRandom.Next()});
    14.         });
    15.     }
    16. }
    17.  
     
  7. tomzigza

    tomzigza

    Joined:
    Aug 4, 2017
    Posts:
    31
    Thanks! This is prob what I was missing. I will have to look at how to ensure this executes before the other one.
     
  8. greyhoundgames

    greyhoundgames

    Joined:
    Jan 24, 2014
    Posts:
    32
    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

    Code (CSharp):
    1. class RandomInitSystem : ComponentSystem
    2. {
    3.   private ComponentGroup randoms;
    4.  
    5.   protected override void OnCreateManager()
    6.   {
    7.     randoms = GetComponentGroup(typeof(InitializeRandom));
    8.   }
    9.  
    10.   protected override void OnUpdate()
    11.   {
    12.     using (NativeArray<Entity> randomsList = randoms.ToEntityArray(Allocator.TempJob))
    13.     {
    14.       foreach (Entity randomEntity in randomsList)
    15.       {
    16.         EntityManager.RemoveComponent<InitializeRandom>(randomEntity);
    17.         EntityManager.AddComponent(randomEntity, typeof(RandomNumbers));
    18.         EntityManager.SetComponentData(randomEntity, new RandomNumbers() { rand = new Unity.Mathematics.Random((uint)UnityEngine.Random.Range(0, 999999)) });
    19.       }
    20.     }
    21.   }
    22. }
     
    Last edited: Jan 29, 2019
  9. Joouur

    Joouur

    Joined:
    Feb 18, 2016
    Posts:
    10
    I don't know if this will help but I usually initialize the entity array as EntityArray

    Code (CSharp):
    1. EntityArrayiterator = randoms.GetEntityArray();
    2. if(iterator.Length == 0) { return; }
    3. NativeArray<Entity> Entities = new NativeArray<Entity>(
    4.   iterator.Length, Allocator.Temp, NativeArrayUnitiliazedMemory
    5. );
    6. iterator.CopyTo(Entities);
    7.  
    8. for(int i=0;i<Entities.Length; ++i)
    9. {
    10.   //Whatever you need to do
    11. }
    12. Entities.Dispose()
    I find it easier to iterated through like this