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

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.

var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, null);
var prefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(axisRef.axis.styles.axisEdgePrefab, settings);
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?

If all you want to do is create an entity in a scene and you don’t want to use ConvertToEntity, then the simple solution is to put your gameobject in a subscene. Here is some documentation on the authoring component workflow and how to use authoring components: Creating gameplay | Entities | 0.8.0-preview.8

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-Technologies/EntityComponentSystemSamples/tree/5fbe871da2ae7f3a173265221017861d2ae6d13c/ECSSamples/Assets/HelloCube/3.%20SubScene) doesn’t make it obvious.

How is this useful for spawning large numbers of objects?

Nevermind, I found the pinned article (ie. New SubScene & ConvertToEntity workflows )

However, this just links back to what I posted.

Seems like ‘ConvertToEntity’ is the approved workflow and all the large examples lean on it to make this actually work.

‘Pure’ mode of spawning entities from entities is clearly a second class citizen.

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.

No, that only works if the prefab is already converted into an entity somehow.

Eg. In https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/ECSSamples/Assets/HelloCube/5.%20SpawnFromEntity/SpawnerSystem_FromEntity.cs, the ‘Prefab’ object is already converted in https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/ECSSamples/Assets/HelloCube/5.%20SpawnFromEntity/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.

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?

2 Likes

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?

I think that’s already the plan: Subscenes and Addressable Asset System

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
[GenerateAuthoringComponent]
public struct PrefabComponent : IComponentData
{
    public Entity Prefab;
}

public struct SpawnTag : IComponentData
{}

public class SpawnSystem : SystemBase
{
    protected override void OnUpdate()
    {
        Entities.WithAll<SpawnTag>().ForEach((Entity entity, ref TestPrefabComponent prefab) =>
        {
            EntityManager.Instantiate(prefab.Prefab);
            EntityManager.RemoveComponent<SpawnTag>(entity);
        }).WithStructuralChanges().WithoutBurst().Run();
    }
}
2 Likes