How to add child to prefab in conversion?

I have this section of code in a conversion method.

foreach(Entity plantPrefab in Plant.InstancePrefabs.Select(conversionSystem.GetPrimaryEntity)) {
    dstManager.AddComponents(plantPrefab, plantComponents);

    Entity rootsPrefab = conversionSystem.CreateAdditionalEntity(Roots);
    dstManager.AddComponents(rootsPrefab, rootComponents);
    dstManager.AddComponent<IsFungus>(rootsPrefab);
    dstManager.SetComponentData(rootsPrefab, new SpeciesDescriptionEntity {Value = entity});
    dstManager.SetComponentData(rootsPrefab, new DescriptionEntity {Value = rootsDescriptionEntity});
    dstManager.SetComponentData(rootsPrefab, new Parent {Value = plantPrefab});
         
    //var linkedEntities = dstManager.AddBuffer<LinkedEntityGroup>(plantPrefab);
    //linkedEntities.Add(new LinkedEntityGroup {Value = rootsPrefab});

    var speciesPrefabs = dstManager.AddBuffer<InstancePrefabs>(entity);
    speciesPrefabs.Add(new InstancePrefabs {Value = plantPrefab});
}

What I want to happen is, create and initialize a new child for the plant prefab, and attach it to the plant, so both are spawned when instantiated.

But with this the roots entity is not spawned, if I uncomment the linkedEntities add and check the entity debugger, only the root entity is added, not the plant entity itself(which is required) to instantiate. It’s like whatever system adds the LinkedEntityGroup ignores the conversion if it already exists, but I’ve tried with Before and After conversion groups.

Can I manufacture prefabs from existing prefabs?

1 Like

Actually, is there anything wrong with using dstManager.Instantiate(ie will anything break/not exist) to copy my plant prefabs?(instead of CreateAdditionalEntity) So I’ll have this hierarchy, which seems to be working in my subscene:

Species instance(prefab)

  • Plant instance(prefab)
  • Roots instance(prefab)

I am also interested in this subject, as I’ve recently faced a number of problems with procedurally generating a subscene during conversion. I’ve encountered issues with live conversion when I created entities manually using dstManager.Instantiate, and a different set of issues with entity versioning when spawning large numbers of entities using CreateAdditionalEntity (editor would slow down and eventually start throwing exceptions, inexplicably CTRL+S would reset it back to workable state).

Additionally, I haven’t really found any way to correctly and performantly spawn complete prefab instances during conversion - I’m surprised that this isn’t a supported workflow (?).

I do suppose this deserves a separate thread but I pretty much gave up on conversion-time spawning and instead I’ve resorted to simply generating a complete GameObject subscene and converting that. Slow as hell, very sadly non-interactive and feels like a huge hack but at least it works.

After lots of experimenting I’ve found that you can’t Instantiate in a conversion system if the entity you’re copying is supposed to have a child, because LinkedEntityGroup doesn’t exist yet.

In systembase if you use Instantiate to copy a prefab, it will create a new entity and child, but the same child will be shared among all parent instances when instantiated later(weird, it does not create copies of the child). If using CopyEntities, it copies the components as expected, but it doesn’t create another child, and the child points to the original parent.

The main question here is, if anyone has an answer. For the purpose of building prefabs from prefabs(who may have children), is it possible to “fork/copy” the entity created from a gameobject in conversion OR, how can we somehow build them at runtime?

1 Like

AFAIK (which is not that much), you need to be using CreateAdditionalEntity. You could copy the ICDs from the first additional entity and set the fields manually but obviously that’s very fragile and not future-proof.
RE building prefabs from prefabs, there may be better ways (i.e. via declareReferencedPrefabs maybe?) but here are a couple of ideas:

  1. On conversion (which can run in the after conversion group) you could create a buffer of the root/main entities of the prefabs you’re referencing. Then have a system at runtime that Instantiates those buffer entries, positions and parents them (if required). This would be how I’d approach it. It should be very fast.
  2. Editor scrips instead of runtime that create the procedural prefabs as large class prefabs which later get converted
1 Like

I realized that I can just go through the Transforms of the prefab and get the entities, then in my conversion I can just manually create the LinkedEntityGroup. It’s not quite what I wanted, since I’ll need to manually create an identical GameObject prefab variant if I want multiple species to use the same object(no generic runtime copying). But it works alright and I can just Instantiate with little set up.

Final-ish conversion:

public override void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) {
    base.Convert(entity, dstManager, conversionSystem);
    dstManager.AddComponent<IsPlant>(entity);

    Entity seedsDescriptionEntity = conversionSystem.GetPrimaryEntity(Seeds);
    Entity rootsDescriptionEntity = conversionSystem.GetPrimaryEntity(Roots);
    Entity plantDescriptionEntity = conversionSystem.GetPrimaryEntity(Plant);
    dstManager.AddComponentData(entity, new SeedDescriptionEntity {Value = seedsDescriptionEntity});
    dstManager.AddComponentData(entity, new RootsDescriptionEntity {Value = rootsDescriptionEntity});
    dstManager.AddComponentData(entity, new PlantDescriptionEntity {Value = plantDescriptionEntity});

    dstManager.AddComponent<IsSeed>(seedsDescriptionEntity);
    dstManager.AddComponent<IsRoots>(rootsDescriptionEntity);
    dstManager.AddComponent<IsPlant>(plantDescriptionEntity);

    dstManager.AddComponentData(entity, LightPreference);
    dstManager.AddComponentData(entity, SoilPreference);
    dstManager.AddComponentData(entity, SpacePreference);
    dstManager.AddComponentData(entity, SeedCountPerStep);

    var speciesPrefabComponents = new ComponentTypes(new ComponentType[] {
        typeof(Prefab), typeof(IsPlant), typeof(Age), typeof(Health), typeof(Fitness),
        typeof(Rotation), typeof(Translation), typeof(Scale), typeof(LocalToWorld)
    });
    var plantPrefabComponents = new ComponentTypes(new ComponentType[] {
        typeof(Prefab), typeof(IsPlant), typeof(SpeciesDescriptionEntity), typeof(DescriptionEntity),
        typeof(LocalToWorld), typeof(LocalToParent), typeof(Parent)
    });
    var rootsPrefabComponents = new ComponentTypes(new ComponentType[] {
        typeof(Prefab), typeof(IsRoots), typeof(SpeciesDescriptionEntity), typeof(DescriptionEntity),
        typeof(LocalToWorld), typeof(LocalToParent), typeof(Parent)
    });
    // Construct completed species instances.
    foreach(GameObject plantGO in Plant.InstancePrefabs) {
        Entity plantPrefab = conversionSystem.GetPrimaryEntity(plantGO);
      
        Entity speciesPrefab = conversionSystem.CreateAdditionalEntity(this);
        dstManager.AddComponents(speciesPrefab, speciesPrefabComponents);
        dstManager.SetComponentData(speciesPrefab, new Scale {Value = 1f});
      
        dstManager.AddComponents(plantPrefab, plantPrefabComponents);
        dstManager.SetComponentData(plantPrefab, new SpeciesDescriptionEntity {Value = entity});
        dstManager.SetComponentData(plantPrefab, new DescriptionEntity {Value = plantDescriptionEntity});
        dstManager.SetComponentData(plantPrefab, new Parent {Value = speciesPrefab});

        Entity rootsPrefab = conversionSystem.CreateAdditionalEntity(Roots);
        dstManager.AddComponents(rootsPrefab, rootsPrefabComponents);
        dstManager.SetComponentData(rootsPrefab, new SpeciesDescriptionEntity {Value = entity});
        dstManager.SetComponentData(rootsPrefab, new DescriptionEntity {Value = rootsDescriptionEntity});
        dstManager.SetComponentData(rootsPrefab, new Parent {Value = speciesPrefab});

        var speciesLinkedGroup = dstManager.AddBuffer<LinkedEntityGroup>(speciesPrefab);
        speciesLinkedGroup.Add(new LinkedEntityGroup {Value = speciesPrefab});
        foreach(Transform transform in plantGO.GetComponentsInChildren<Transform>()) {
            Entity foundEntity = conversionSystem.GetPrimaryEntity(transform.gameObject);
            speciesLinkedGroup.Add(foundEntity);
        }
        speciesLinkedGroup.Add(new LinkedEntityGroup {Value = rootsPrefab});

        var speciesPrefabs = dstManager.AddBuffer<InstancePrefabs>(entity);
        speciesPrefabs.Add(new InstancePrefabs {Value = speciesPrefab});
    }
}
1 Like

[RecursiveEclipse]( How to add child to prefab in conversion? members/recursiveeclipse.1990768/) This solution is sooo close, but as part of the MeshFilter conversion, multiple entities get created per-material, and GetPrimaryEntity(transform) only yields the single parent entity. Were you able to find a workaround for this?