GameObjects Conversion and Parenting Questions [RESOLVED]

Hi everyone !

knowing that it’s impossible for the moment instantiating Pure entities containing Particale System or any MonoBehaviour Component, so im trying to make hybred entities with needed MonoBehaviours following my pure Entities, like a particale system following a bullet …

so my questions are:
1) is there any easy way of making a Hybred entity following a Pure one ?

im using the IConvertGameObjectToEntity and it’s GameObjectConversionSystem variable within the Convert inherited methode to convert my Prefab but the returned entity doesn’t contain my MonoBehaviour Components but just all the Proxies as IComponentData.
2) is it possible to create a fast System of instantiating hybred Entities with MonoBehaviour Components?

  1. Not that I know of, but you don’t really need that. There is nothing stopping you from instantiating a pure entity, and then using EntityManager.AddComponentObject to “subscribe” a UnityEngine Component or Monobehavior from elsewhere in the scene to that pure entity.
  2. I highly doubt it. You would probably have to pool the UnityEngine Components/MonoBehaviours.

For your bullet entity, just instantiate the pure entity bullet, have a GameObject with the desired Particle System in your scene, add the Transform and Particle System of the GameObject to the pure entity via EntityManager.AddComponentObject, and add a CopyTransformToGameObject Component Data to the bullet entity.

i tried your suggestion but still not working :frowning:

i added all Components of my bullet prefab to the Pure entityusing the AddComponentObject as you said and i see them via the Entity Debugger but they are not rendering i really dont understand why.

public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        /*
        var spawnerData = new EntitySpawner
        {
            // The referenced prefab will be converted due to DeclareReferencedPrefabs.
            // So here we simply map the game object to an entity reference to that prefab.
            Prefab = conversionSystem.GetPrimaryEntity(Prefab),
            CountX = CountX,
            CountY = CountY
        };
        dstManager.AddComponentData(entity, spawnerData);*/


        var entityInstance = conversionSystem.GetPrimaryEntity(Prefab);
       
        var components = Prefab.GetComponents<Component>();

        for (var i = 0; i != components.Length; i++)
        {
            var component = components[i];
            var proxy = component as ComponentDataProxyBase;
            var behaviour = component as Behaviour;
            if (behaviour != null && !behaviour.enabled)
                continue;

            if (!(component is GameObjectEntity) && component != null && proxy == null)
            {
                //var X = ComponentType.ReadWrite<ParticleSystem>();
                dstManager.AddComponentObject(entityInstance, component);
            }
        }

        var spawnerData = new EntitySpawner
        {
            // The referenced prefab will be converted due to DeclareReferencedPrefabs.
            // So here we simply map the game object to an entity reference to that prefab.
            Prefab = entityInstance,
            CountX = CountX,
            CountY = CountY
        };
        dstManager.AddComponentData(entity, spawnerData);
    }

You should use a Component System to pair the Component Objects with the entities, not IConvertGameObjectToEntity

whats the syntax usage for CopyTransformToGameObject ??

until now i found no solution to instantiate Particale Systems that are linked to entities in an efficient way.
does anyone have a suggestion?

I found a good and efficient way To instantiate and move GameObjects ( Particale System ) and make them follow Projectiles (Pure Entities).

i created 2 Components that manage Attaching, Detaching and Following Logic.

/// Manage Attachement States
/// System States :
/// (+) AttachementEffect, (-) AttachementSystemState : Projectile Entity Require Attachment.
/// (+) AttachementEffect, (+) AttachementSystemState : The GameObject Can Follow the Projectile Entity.
/// (-) AttachementEffect, (+) AttachementSystemState : The GameObject Attached need to be Recycled.
[System.Serializable]
public struct AttachmentEffectSystemState : ISystemStateComponentData
{
public byte EffectId;
public int RefIndex;
}

[System.Serializable]
public struct AttachmentEffect : IComponentData
{
public byte EffectId;
}

“ProjectileSpawnerSystem” Will Spawn entities as Projectile adding AttachmentEffect, Rotation, Translation.

“AttachmentEffectsManagerSystem” will Attach,Dettach & Follow GameObjects to Entities

    [UpdateInGroup(typeof(SimulationSystemGroup))]
    [UpdateAfter(typeof(ProjectileSpawnerSystem))]
    public class AttachmentEffectsManagerSystem: ComponentSystem
    { 
        GameObjectPool gameObjectPool;
        ObjectHolder objectHolder;



        protected override void OnCreate()
        {
         
            gameObjectPool = new GameObjectPool();
        }

        protected override void OnUpdate()
        {
            // Attachment Effects to Recycle
            Entities.
                WithNone<AttachmentEffect>().
                WithAllReadOnly<AttachmentEffectSystemState>().
                ForEach((Entity e, ref  AttachmentEffectSystemState attachementEffectSystemState) => {
                    gameObjectPool.RecycleEffect(attachementEffectSystemState.EffectId, attachementEffectSystemState.RefIndex);
                    PostUpdateCommands.RemoveComponent<AttachmentEffectSystemState>(e);
                    PostUpdateCommands.DestroyEntity(e);
                });



            // Move Effects 
            Entities.
                WithAllReadOnly<AttachmentEffectSystemState, AttachmentEffect, Translation>().
                ForEach(( ref AttachmentEffectSystemState attachementEffectSystemState, ref Translation translation) => {
                    gameObjectPool.MoveObject(attachementEffectSystemState.RefIndex, translation.Value);
                });



            // Attachment Effects to Link
            Entities.
             WithNone<AttachmentEffectSystemState>().
             WithAllReadOnly<AttachmentEffect, Rotation, Translation>().
             ForEach((Entity entityProjectile, ref AttachmentEffect attachementEffect, ref Rotation rotation, ref Translation translation) => {
                 objectHolder = gameObjectPool.GetEffect(attachementEffect.EffectId, translation.Value, rotation.Value, out var indexRef);
                 PostUpdateCommands.AddComponent(entityProjectile, new AttachmentEffectSystemState { EffectId = attachementEffect.EffectId, RefIndex = indexRef });
             });
        }
    }

“ProjectileMoverSystem” Will Move Projectiles Entities and create Raycasts

GameObjectPool Class is Pool System that Manage Instantiating,Pooling and moving the GameObjects Effects.

  public class GameObjectPool 
    {
        public static GameObjectPool instance;

        private Queue<int> HolesIndexes;
        private List<ObjectHolder> activeGameObjects;

        private Dictionary<byte, Queue<ObjectHolder>> pool;
        
        public GameObjectPool()
        {
            HolesIndexes = new Queue<int>();
            activeGameObjects = new List<ObjectHolder>();
            pool = new Dictionary<byte, Queue<ObjectHolder>>();
            instance = this;

            //  Addressables.LoadAssetAsync<GameObject>("WEAId12");
             Addressables.LoadAssetsAsync<GameObject>(AddressablesLabels.ParticaleSystems, null).Completed += objects =>
             {
                 foreach (var go in objects.Result)
                     Debug.Log($"Addressable Loaded: {go.name}");
             };
            
        }


        /// <summary>
        /// Get a GameObject From Pool
        /// Rotation and Postion are needed to Fix Trail and sound effects
        /// </summary>
        /// <param name="EffectId"></param>
        /// <param name="rotation"></param> 
        /// <param name="indexRef"></param>
        /// <returns></returns>
        public ObjectHolder GetEffect(byte EffectId, float3 pos, quaternion rot,  out int indexRef)
        {
            Queue<ObjectHolder> objecQueue;
            // This Object type has already been used
            if (pool.ContainsKey(EffectId))
            {
                objecQueue = pool[EffectId];
            }
            else
            {
                objecQueue = new Queue<ObjectHolder>();
                pool.Add(EffectId, objecQueue);
            }


            ObjectHolder instance;
            // Queue Has Instances
            if (objecQueue.Count > 0)
            {
                // Get Recycled Instance
                instance = objecQueue.Dequeue();
                instance._transform.rotation = rot;
                instance._transform.position = pos;
                instance._gameobject.SetActive(true);
               /* for(var i = 0; i< instance.particleSystemsIchilds.Length; i++)
                {
                    instance.particleSystemsIchilds[i].Play();
                }*/
            }
            else // Empty Queue
            {
                var go = Addressables.Instantiate(GameBootstrap.WeaponsEffectsAddressablePrefix + EffectId ,  pos, rot).Result;
                instance = go.GetComponent<ObjectHolder>();
               /* for (var i = 0; i < instance.particleSystemsIchilds.Length; i++)
                {
                    instance.particleSystemsIchilds[i].Play();
                }*/
            }

            // Check for indexRef Gaps
            if (HolesIndexes.Count > 0)
            {
                indexRef = HolesIndexes.Dequeue();
                activeGameObjects[indexRef] = instance;
            }
            else
            {
                activeGameObjects.Add(instance);
                indexRef = activeGameObjects.Count-1;
            }

            return instance;
        }

        /// <summary>
        /// Recycle an Effect
        /// </summary>
        /// <param name="EffectId"></param>
        /// <param name="indexRef"></param>
        public void RecycleEffect(byte EffectId, int indexRef)
        {
            var objectHolder = activeGameObjects[indexRef];
            activeGameObjects[indexRef] = null;
            objectHolder._gameobject.SetActive(false);
            HolesIndexes.Enqueue(indexRef);
            pool[EffectId].Enqueue(objectHolder);
        }

        /// <summary>
        /// Move an Active GameObject
        /// </summary>
        /// <param name="index"></param>
        /// <param name="position"></param>
        public void MoveObject(int index,float3 position)
        {
            activeGameObjects[index]._transform.position = position;
        }

        // Clear all Effects Within the Pool
        public void CleanPool()
        {
            for (var i = 0; i < activeGameObjects.Count; i++)
            {
                if (activeGameObjects[i] != null)
                {
                    Addressables.ReleaseInstance(activeGameObjects[i]._gameobject);
                }

                activeGameObjects.Clear();
            }

            foreach (var Q in pool.Values)
            {
                for (var i = 0; i < Q.Count; i++)
                {
                    Addressables.ReleaseInstance(Q.Dequeue()._gameobject);
                }
            }
        }


    }

Im still having a Question.
why even by using WithAllReadOnly<AttachmentEffectSystemState, AttachmentEffect, Translation> all these components are still accessed as RW ?

// Move Effects
            Entities.
                WithAllReadOnly<AttachmentEffectSystemState, AttachmentEffect, Translation>().
                ForEach(( ref AttachmentEffectSystemState attachementEffectSystemState, ref Translation translation) => {
                    gameObjectPool.MoveObject(attachementEffectSystemState.RefIndex, translation.Value);
                });

Hey man,

I was wondering how happy you’re with this solution after 1 year. If not, did you find a better way?
I’m asking because I’m about to do something very similar with blob shadows :slight_smile:

Thanks!

1 Like

Hey,
Yes I’m still using this solution which i optimized a little bit and now it has practically no cost, but recently unity added an easier way I haven’t tested yet. Which is adding your components( ParticleSystems…) as companions. Using conversionSystem.AddHybridComponent(component). This way when you instantiate an entity having hybrid components the EntityManager automatically copies all hybrid components related to that entity. So you don’t have to manage the hybrid components anymore, this way you can freely instantiate your hybrid entities within jobs.
The bad side, is it’s impossible for you to create pooling anymore. ( But people in this forum said that it’s already optimized and no pooling is needed )
The solution I’m using gives you the possibility to instantiate entities from jobs too but after that instantiation other ComponentSystems are charged to manage the pooling part.

1 Like

Thanks for the reply. I’m using HybridComponents for ParticleSystems and TrailRenderers already. For my blob shadows this SystemState based approach seems to work quite well: https://forum.unity.com/threads/systemstatecomponent-use-case-question.889201/#post-5846722

But now I’m thinking I could also just attach the blob shadow quad to all my prefabs and tag them with a Shadow component that handles the update part, while letting let the “auto-conversion linking” handle the create and destroy part :hushed:

As usual there are so many ways to do the same thing lol but in general still a big fan of this pattern. Feels super clean and reusable :slight_smile:

1 Like

Good Luck with your project :wink:

1 Like

Hey have you guys ever noticed horrible lag with your hybrid components? My entities are supposed to be moving around while they’re on fire… but instead they look like they’re pooping fire. In release mode I’m getting 200+fps, but it seems like the hybrid transform updates are just lagging really bad.

I updated to 0.50 and the gameobjects are still really laggy.

[ EDIT - fixed! I had to change the particle simulation space from World to Local.]

1 Like