Particle System on Entity

I have a prefab bullet that was converted to an entity (ConvertToEntity).
I can’t figure out how to add ParticleSystem to it.
I see it in the debugger listed as a child like I have in the prefab but it doesn’t render.
Any ideas?
Pretty new to this.
No code currently available. Hope I won’t need it :slight_smile:

Thanks

1 Like

Did you find a solution for this?

Attach a tag"HybridParticle" to the bullet when created to identify it in a query. Look for it in a monobehaviour LateUpdate using a query. Note that I store the entity as a Vector2Int where x = index and y = version. If not in the matching indexed Lists or Dictionary object then kill the particles. Then those it hasn’t killed it queries their translation and sets the particle position by calling UpdateParticleSystem()…

public class HybridParticleSystem : MonoBehaviour {
    [ShowInInspector]
    public Dictionary<Vector2Int, Tuple<int, GameObject>> objects;
    public Vector2Int entityIDVers;
    public Vector2Int entityID;
    public List<Vector2Int> attachingEntities;
    public List<GameObject> attachedParticleSystems;
    private EntityManager manager;
    private EntityQuery query;
    public ParticleEfxPool particleEfxPool;

    void Start () {
        objects = new Dictionary<Vector2Int, Tuple<int, GameObject>> ();
        manager = World.DefaultGameObjectInjectionWorld.EntityManager;
        query = manager.CreateEntityQuery (ComponentType.ReadOnly<Translation> (), ComponentType.ReadOnly<HybridParticle> ());
        query.SetChangedVersionFilter (typeof (Translation));
    }

    public void AddParticleSystem (Entity entity, Translation translation, GameObject obj, int poolIndex) {
        //set the game object transform
        obj.transform.position = translation.Value;
        obj.transform.rotation = Quaternion.identity;
        //add to the dictionary
        entityIDVers.x = entity.Index;
        entityIDVers.y = entity.Version;
        objects[entityIDVers] = Tuple.Create (poolIndex, obj);
        attachingEntities.Add (entityIDVers);
        attachedParticleSystems.Add (obj);
    }

    private void UpdateParticleSystem (Entity entity, Translation translation, HybridParticle particle) {
        entityIDVers.x = entity.Index;
        entityIDVers.y = entity.Version;
        var obj = objects[entityIDVers].Item2;
        obj.transform.position = translation.Value;
    }

    public void CullUnattachedParticleSystems (Entity culledEntity) {
        entityID.x = culledEntity.Index;
        entityID.y = culledEntity.Version;
        var obj = objects[entityIDVers].Item2;
        for (int i = 0; i < attachingEntities.Count; i++) {
            if (entityID == attachingEntities[i]) {
                Destroy (attachedParticleSystems[i]);
                attachingEntities.RemoveAt (i);
                attachedParticleSystems.RemoveAt (i);

            }
        }

       
    }

    private void LateUpdate () {
        var entities = query.ToEntityArray (Allocator.TempJob);
        if (entities.Length == 0) {
            //remove all particle refs
            foreach (var poolObj in objects.Values) {
                if (poolObj.Item2) { Destroy (poolObj.Item2); }
            }
            objects = new Dictionary<Vector2Int, Tuple<int, GameObject>> ();
        }
        for (int i = 0; i < entities.Length; i++) {
            var entity = entities[i];
            UpdateParticleSystem (entity, manager.GetComponentData<Translation> (entity), manager.GetComponentData<HybridParticle> (entity));
        }
        entities.Dispose ();
    }
}

Have fun!

2 Likes

Wow Really useful stuff.
Thanks!

There are some lines in there that relate to other systems I have in place, but the gist of it is there.Generally just call AddParticleSystem(…params) and pass the params. I thank the others who helped me so just paying back the favor.

@ippdev is the reason to run the sync from the MonoBehaviour side because it is more efficient than having structural changes by going through
EntityManager.AddComponentObject(entity, gameObjectPrefabInstance.transform); and having a CopyTransformToGameObject?

Latter one feels a bit cleaner I think :face_with_spiral_eyes:

Take a look here:

I guess the new AddHybridComponent is what you’re looking for

Could be. There are many parts of the API I haven’t explored yet. I do know the bridging works and it costs nearly nothing on the ECS side as it is an empty tag and the branching and for loop in LateUpdate are minimal footprint on the mono side… But you are doing more than copy a transform. You have to identify entities that are no longer there to destroy or return to the pool the particle system. The code I pasted could be cleaned up further to use matched indexed List or the Dictionary.

@Srokaaa see my response over there. It seems to work very very well (For Trail Renderers, didn’t try Particle Systems yet) :slight_smile:

@ippdev Thanks again for your replies. I recommend taking a look at @Srokaaa 's link: Convert And Inject Game Object in Prefabs

Sighs, it turns out ParticleSystems don’t work when I do exactly the same AddHybridComponent thing that worked with the Trail Renderers, lol… anyone has an idea why that would be? :hushed:

(It does work with AddComponentObject like a charm)

Merp, turns out you need to add both the ParticleSystem and the ParticleSystemRenderer in your IConvertGameObjectToEntity.

public class TestPrefabAssetRegistryEntryAuthoring : MonoBehaviour, IConvertGameObjectToEntity {
   public void Convert(Entity entity, EntityManager entityManager, GameObjectConversionSystem gameObjectConversionSystem) {
      if (TryGetComponent<ParticleSystem>(out var particleSystem)) {
         gameObjectConversionSystem.AddHybridComponent(particleSystem);
      }
      if (TryGetComponent<ParticleSystemRenderer>(out var particleSystemRenderer)) {
         gameObjectConversionSystem.AddHybridComponent(particleSystemRenderer);
      }
   }
}

Haven’t done performance tests, but eyeballing it, it pooling doesn’t seem super necessary either :slight_smile:

2 Likes

So I was using this exact conversion system for a year now and it worked just fine… it was super easy because anytime i instantiated a prefab that had particle effects it would automatically add them and keep the particles “tethered” to the entity.

But now… in 0.50, AddHybridComponent() is RIP. So does that mean we have to go back to running our own systems with translation mimicking and system-state components for handling destruction?

From HybridEntitiesConversion.cs in latest v0.50 Hybrid Renderer package:

            Entities.ForEach((ParticleSystem ps, ParticleSystemRenderer ren) =>
            {
                var entity = GetPrimaryEntity(ps);
                DstEntityManager.AddComponentObject(entity, ps);
                DstEntityManager.AddComponentObject(entity, ren);
            });

I haven’t tried yet, but, putting Particle System in a subscene should work straight away, without any conversion code.

AddHybridComponent is gone. I haven’t checked how parenting and translation/rotation will work now.

1 Like

That looks to me like it should work.

Maybe it’s a separate issue, but I can make a simple prefab, load it via addressables into a variable called prefabEntity, and then call call EntityManager.Instantiate(prefabEntity) and everything works great. As soon as I add a Light or ParticleSystem to the gameobject prefab, this error happens:

runtime error

MissingReferenceException: The object of type ‘GameObject’ has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UnityEngine.Object.Instantiate[T] (T original) (at <3be1a7ff939c43f181c0a10b5a0189ac>:0)
Unity.Entities.CompanionLink.Clone () (at Library/PackageCache/com.unity.entities@0.50.0-preview.24/Unity.Entities.Hybrid/Components/CompanionLink.cs:30)
Unity.Entities.ManagedObjectClone.Clone (System.Object obj) (at Library/PackageCache/com.unity.entities@0.50.0-preview.24/Unity.Entities/Properties/ManagedObjectClone.cs:61)
Unity.Entities.ManagedComponentStore.CloneManagedComponents (System.Int32* srcArray, System.Int32 componentCount, System.Int32* dstArray, System.Int32 instanceCount) (at Library/PackageCache/com.unity.entities@0.50.0-preview.24/Unity.Entities/ManagedComponentStore.cs:746)
Unity.Entities.ManagedComponentStore.Playback (Unity.Entities.ManagedDeferredCommands& managedDeferredCommands) (at Library/PackageCache/com.unity.entities@0.50.0-preview.24/Unity.Entities/ManagedComponentStore.cs:689)
Unity.Entities.EntityDataAccess.PlaybackManagedChangesMono () (at Library/PackageCache/com.unity.entities@0.50.0-preview.24/Unity.Entities/EntityDataAccess.cs:1257)
Unity.Entities.EntityDataAccess.PlaybackManagedDirectly (System.Boolean& didTheThing) (at Library/PackageCache/com.unity.entities@0.50.0-preview.24/Unity.Entities/EntityDataAccess.cs:1264)
Unity.Entities.EntityDataAccess.PlaybackManagedChanges () (at Library/PackageCache/com.unity.entities@0.50.0-preview.24/Unity.Entities/EntityDataAccess.cs:1273)
Unity.Entities.EntityDataAccess.EndStructuralChanges (Unity.Entities.EntityComponentStore+ArchetypeChanges& changes) (at Library/PackageCache/com.unity.entities@0.50.0-preview.24/Unity.Entities/EntityDataAccess.cs:390)
Unity.Entities.EntityManager.Instantiate (Unity.Entities.Entity srcEntity) (at Library/PackageCache/com.unity.entities@0.50.0-preview.24/Unity.Entities/EntityManager.cs:1928)
InputController.OnBeginDropping (BeginDroppingEvent evnt) (at Assets/Scripts/Controllers/InputController.cs:145)

Removing the light or particle system makes it work just fine again, so it’s something related to companion game objects. I dunno… That error didn’t show up until 0.50 and I thought it was related to not using AddHybridComponent() anymore, but maybe it’s a separate issue.

2 Likes

Some examples/samples from Unity on how to work now with important Components which do not have any IComponentData representation will surely help, like ParticleSystem, Animator, TextMesh etc.

2 Likes

So I learned that the problem is coming from the fact that I am using addressables to load the prefabs in the introductory title-scene and then when game-play starts, the companion objects for particle-systems, lights, etc get deleted. Using AddComponentObject() in GameObjectConversionSystem does not help because it gets called in the title-scene and not in the game-scene.

I’m sorta stuck… The only thing I can think of is to stop using a title-scene and do all the asset-loading at the beginning of the game-play scene with the game paused until the async asset loading finishes so systems don’t run and try to instantiate things that don’t exist yet. How are you guys loading prefab assets?

[Edit - I found an ugly hacky workaround. It sucks, but it’s better than nothing.]