Search Unity

How to create an Entity: unexpectedly difficult. What am I missing?

Discussion in 'Entity Component System' started by xshadowmintx, Apr 12, 2020.

  1. xshadowmintx

    xshadowmintx

    Joined:
    Nov 4, 2016
    Posts:
    47
    I've found that process for creating an entity is unexpectedly difficult.

    Practically speaking, the process currently appears to be:

    a) Use a MonoBehaviour and spawn a prefab with ConvertToEntity on it.

    or, if that isn't suitable for any number of reasons:
    • Create an X : ISharedComponentData object to reference any resources (eg. Mesh, Material, etc).
    • Create a Y : IComponentData object to track the existence of required objects.
    • Create a Z : SystemBase
    In Z, use the WithStructuralChanges().WithoutBurst().Run() special qualifiers to allow entity creation to happen (note, you can also use a command buffer to defer the process, see https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/ECSSamples/Assets/HelloCube/5. SpawnFromEntity/SpawnerSystem_FromEntity.cs, but that's even more complicated).

    In Z's activity, take a reference X (reference data) and Y (rules), note specifically to use:
    (X shared, ref Y rules) => {} in the lambda, because apparently (due to technical limitations according to some obscure forum thread?) shared component data must be listed before component data in the lambda syntax.

    You can now run the operation to actually create an entity.

    However, if using a prefab to construct the entity, EntityManager.Instantiate will not automatically detect the entity data attached to the prefab. Either manually assign every single instance of ComponentData to the entity instance created, or, as done in the examples (https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/d78b0ff7ad5bb0cac0d3ad18c4c2e9bf114d0c50/ECSSamples/Assets/HelloCube/4. SpawnFromMonoBehaviour/Spawner_FromMonoBehaviour.cs), you must first convert the GameObject prefab into an entity prefab.

    Code (CSharp):
    1. var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, null);
    2. var prefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(axisRef.axis.styles.axisEdgePrefab, settings);
    3. entityManager.SetName(prefab, "AxisTemplateEntity");
    However, note that in doing so you actually create an entity instance (viewable in the debugger); therefore, the created entity should be cached on your shared component data (X), otherwise you will create two entities every attempt you make to spawn objects.

    Finally, entityManager.Instantiate(entityPrefab) will generate a new prefab, and you can use
    SetComponentData (note: Not AddComponentData; the entity instance from the prefab will already have the data; use AddComponentData if you manually build the entity from an Archetype).

    This matches, it appears, what ConvertToEntity does on a game object.

    ie.
    • 1 x SharedComponentData for reference data
    • 1 x ComponentData for recipe
    • 1 x System to spawn
    • Convert prefab to entity prefab, cache result
    • Generate entities using entityManager.Instantiate(entityPrefab)
    • Update the recipe with a reference ID so you don't just endlessly spawn entities
    • Do not forget correct invokation (WithStructuralChanges, etc).
    So, anyway, this feels rather unexpectedly difficult and painful for what should be a trivial operation.

    I had a good look at the examples too, and well, that seems to be how you do it.

    (and heck, maybe this is even wrong, since the examples use
    IDeclareReferencedPrefabs, IConvertGameObjectToEntity to convert the prefab into an entity prefab, even though its done as per the code snippet above in other places in the same project..?)

    I guess, am I missing something?

    Is there a helper library somewhere that people use for this stuff?
     
  2. desertGhost_

    desertGhost_

    Joined:
    Apr 12, 2018
    Posts:
    260
  3. xshadowmintx

    xshadowmintx

    Joined:
    Nov 4, 2016
    Posts:
    47
    What's a sub scene?

    It's not documented anywhere obvious.

    The link you've given talks about it, but that's it. The example (https://github.com/Unity-Technologi...6d13c/ECSSamples/Assets/HelloCube/3. SubScene) doesn't make it obvious.

    How is this useful for spawning large numbers of objects?
     
  4. xshadowmintx

    xshadowmintx

    Joined:
    Nov 4, 2016
    Posts:
    47
  5. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,267
    This sounds like you are doing something wrong. EntityManager.Instantiate should copy any values of the prefab components into the components of the newly created entity.
     
  6. xshadowmintx

    xshadowmintx

    Joined:
    Nov 4, 2016
    Posts:
    47
    No, that only works if the prefab is already converted into an entity somehow.

    Eg. In https://github.com/Unity-Technologi.... SpawnFromEntity/SpawnerSystem_FromEntity.cs, the 'Prefab' object is already converted in https://github.com/Unity-Technologi...FromEntity/SpawnerAuthoring_FromEntity.cs#L28 to an entity prefab.

    All the examples do it as I've described.

    EntityManager.Instantiate does not work with regular prefabs.
     
  7. JakHussain

    JakHussain

    Joined:
    Oct 20, 2016
    Posts:
    318
    I think you are hitting on something important. I have a suggestion for someone in the DOTS team to consider:

    Much like how subscenes are written to disk in a runtime ready format to be instantly loaded as a cache of entities, prefabs in the project window should have a "convert to entity prefab" button on them to either converts the asset or makes a second one beside it which is the SAME runtime ready format for fast loading and unloading. Then via addressable assets, the new serialised entity prefab asset with all its preconverted shared component data and regular component data can be referenced in whatever system so that EntityManager.Instantiate() or commandbuffer.Instantiate() is all that needs to be called.

    This would be a pretty ideal solution as it takes the conversion workflow out of the scene view and out of playmode entirely when it comes to runtime instancing. I would consider this workflow a much closer representation of "pure DOTS".

    What do you guys think?
     
    varnon and KwahuNashoba like this.
  8. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,267
    So you are frustrated that you cannot use EntityManager.Instantiate on a GameObject prefab that has not been converted to an Entity? What would even be the point of that?

    If you want a GameObject to be an Entity, you have to convert it. That means either using GameObjectConversionUtility, ConvertToEntity, a subscene, or having a referencer of the GameObject prefab do one of those things with an IDeclareReferencedPrefabs. Once it the prefab is an entity prefab, you can do whatever the heck you want with it.

    What exactly are you trying to do?
     
  9. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,267
    I think that's already the plan: Subscenes and Addressable Asset System
     
  10. Flipps

    Flipps

    Joined:
    Jul 30, 2019
    Posts:
    51
    I think the big problem is the lack of really good documentation to perform different scenarios like
    - Spawn from prefab
    - Parent / Unparenting
    - How to reference other Entities and clear these Entities together
    - Handle different hybrid cases (NavMesh, Animation, UI)
    etc.

    For the example above i think the easiest way is to use these 3 scripts:
    - Empty GameObject with PrefabComponent + ConvertToEntity scripts attached
    (the GenerateAuthoring will Convert the GameObject to an Entity Prefab)
    - Add SpawnTag to Entity with PrefabComponent to Spawn something

    Code (CSharp):
    1. [GenerateAuthoringComponent]
    2. public struct PrefabComponent : IComponentData
    3. {
    4.     public Entity Prefab;
    5. }
    6.  
    7. public struct SpawnTag : IComponentData
    8. {}
    9.  
    10. public class SpawnSystem : SystemBase
    11. {
    12.     protected override void OnUpdate()
    13.     {
    14.         Entities.WithAll<SpawnTag>().ForEach((Entity entity, ref TestPrefabComponent prefab) =>
    15.         {
    16.             EntityManager.Instantiate(prefab.Prefab);
    17.             EntityManager.RemoveComponent<SpawnTag>(entity);
    18.         }).WithStructuralChanges().WithoutBurst().Run();
    19.     }
    20. }
     
    lclemens and chrismarch like this.