Search Unity

Convert And Inject Game Object in Prefabs

Discussion in 'Entity Component System' started by Srokaaa, Jan 9, 2020.

  1. Srokaaa

    Srokaaa

    Joined:
    Sep 18, 2018
    Posts:
    169
    Is there a standard way of instantiating an Entity that has a child with ConvertToEntity-(Convert And Inject Game Object) somewhere in it's hierarchy?

    Example:
    I have a PlayerShip prefab and one of it's children is an Engine GameObject with with Visual Effect component so that I may use VFX Graph to draw engine exhaust.
    Currently I set ConvertToEntity-(Convert And Destroy) component on a PlayerShip and ConvertToEntity-(ConvertInject Game Object) and CopyTransformToGameObject components on Engine.

    Situation 1 - Instantiating by putting a prefab in a scene
    This works just fine. Whole hierarchy is converted to entities destroyed but Engine which is just converted and has it's transform copied every turn form accompanying entity.


    Situation 2 - Instantiating by calling EntityCommandBuffer.Instantiate()
    Sometimes I don't need my PlayerShip prefab spawned right away but rather as a result of some event. So I made PlayerSpawner IComponentData and accompanying PlayerSpawnerAuthoring MB with IDeclareReferencedPrefabs. Unfortunately when I spawn my PlayerShip prefab with EntityCommandBuffer.Instantiate() entities are spawned correctly but Engine GameObject that my VFX Graph is attached to doesn't.

    Is there a way to instantiate GameObjects in Situation 2?
    Is there an easy way to use VFX graph with Unity ECS atm or do we have to wait longer for it?
     
    linq likes this.
  2. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    The standard way is by calling AddHybridComponent while in conversion, and that will create a "companion game object" hosting those selected components to go together with your convertible parts. You can use the convert and destroy mode at the top of ship with nothing special or any Stops on any of the child.

    As a part of conversion, get a conversion mapper system (in your GameObjectConversionSystem or in your IConvertGameObjectToEntity) and call .AddHybridComponent to add your VFX component from down the tree. The result is that the primary entity that would be generated from game object of that component would have an additional CompanionLink component that link to a hidden new game object with VFX component migrated to it. That is the companion game object created as a part of conversion. The hidden companion game object then has a system similar to copy transform to game object to sync its classic Transform position to that primary entity.

    If used on a prefab, then the result will contains Prefab + LinkedEntityGroup + CompanionLink. The CompanionLink make it EntityManager.Instantiate compatible, it will also duplicate the hidden companion game object making it seamless that also the not convertible part could get instantiated together with pure ECS part.
     
  3. Srokaaa

    Srokaaa

    Joined:
    Sep 18, 2018
    Posts:
    169
    @5argon You, sir, are a hero :) Works like a charm and in general AddHybridComponent solves most of the problems I ever had with interop between ECS and MonoBehaviors
     
    shotoutgames likes this.
  4. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Just to add that I may have found a bug about AddHybridComponent when having multiple things to save to companion object. (not sure but already submitted : https://fogbugz.unity3d.com/default.asp?1217971_hehg8e7i5msfhrjc) For example this cube where I would like to keep 2 nested TextMeshPro after conversion. I have written like following in order to save everything required of both :

    upload_2020-2-8_22-31-6.png

    Code (CSharp):
    1. using TMPro;
    2. using Unity.Entities;
    3. using Unity.Transforms;
    4. using UnityEngine;
    5.  
    6. internal class GridBarAuthoring : MonoBehaviour, IConvertGameObjectToEntity
    7. {
    8. #pragma warning disable 0649
    9.     [SerializeField] private TextMeshPro leftText;
    10.     [SerializeField] private TextMeshPro rightText;
    11. #pragma warning restore 0649
    12.  
    13.     public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    14.     {
    15.         if (leftText != null)
    16.         {
    17.             var go1 = leftText.gameObject;
    18.             conversionSystem.AddHybridComponent(leftText);
    19.             conversionSystem.AddHybridComponent(go1.GetComponent<RectTransform>());
    20.             conversionSystem.AddHybridComponent(go1.GetComponent<MeshRenderer>());
    21.             conversionSystem.AddHybridComponent(go1.GetComponent<MeshFilter>());
    22.             conversionSystem.AddHybridComponent(go1.GetComponent<CanvasRenderer>());
    23.         }
    24.  
    25.         if (rightText != null)
    26.         {
    27.             var go2 = rightText.gameObject;
    28.             conversionSystem.AddHybridComponent(rightText);
    29.             conversionSystem.AddHybridComponent(go2.GetComponent<RectTransform>());
    30.             conversionSystem.AddHybridComponent(go2.GetComponent<MeshRenderer>());
    31.             conversionSystem.AddHybridComponent(go2.GetComponent<MeshFilter>());
    32.             conversionSystem.AddHybridComponent(go2.GetComponent<CanvasRenderer>());
    33.         }
    34.     }
    35. }
    But turns out when both were to be converted, I always got an error about right one failing to convert. On attach code debugging it seems the left one got all 5 components saved but only RectTransform for the right one, so it attempts to destroy e.g. CanvasRenderer and then it error because other component has required on it.

    If I convert only one of them then it works fine. Maybe this bug only occurs if you have 2 similar things on the same conversion to migrate.
     
    nicolasgramlich likes this.
  5. Flipps

    Flipps

    Joined:
    Jul 30, 2019
    Posts:
    51
    Is AddHybridComponent the new workflow to convert all none-convertible Monobehaviours?
    Mostly:
    - Animator
    - Camera
    - UI (Canvas, Text etc)
    - Particle Systems

    When did Unity release this feature?
     
  6. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    Last edited: Feb 9, 2020
    psuong, nicolasgramlich and Flipps like this.
  7. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    Hybrid Components are an experimental feature, their use isn't recommended yet.


    So tempting.... :rolleyes:
     
  8. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    Wow, that was easy :). Eyeballing the components it creates internally, it looks it's doing almost exactly the same thing that I was planning to do.

    The only downside is that probably nothing is pooled... :rolleyes::eek:

    That being sad without any further optimization I'm having 441 archers firing once per second on 441 targets (resulting in ≈3*441=1320 Trail Renderers visible at any time)...

    90 effing FPS on a Pixel 4XL and rock solid 45 FPS on a 3 year old Pixel XL (for a release build, not even master) :):):)

    Screenshot_20200220-224655.png

    Things scale so well with ECS, it's just nasty :D:rolleyes::p

    Edit:
    Sorry, this makes me so happy :):):)

    2000 units, 3000 Trail Renderers, Pixel 4XL, 60FPS
    Screenshot_20200220-230152.png
     
    Last edited: Feb 21, 2020
  9. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    Sighs, it turns out ParticleSystems don't work when I do exactly the same thing that worked with the Trail Renderers, lol... anyone has an idea why that would be? :eek:
     
  10. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    Merp, turns out you need to add both the
    ParticleSystem
    and the
    ParticleSystemRenderer
    in your
    IConvertGameObjectToEntity
    .


    Code (CSharp):
    1. public class TestPrefabAssetRegistryEntryAuthoring : MonoBehaviour, IConvertGameObjectToEntity {
    2.    public void Convert(Entity entity, EntityManager entityManager, GameObjectConversionSystem gameObjectConversionSystem) {
    3.       if (TryGetComponent<ParticleSystem>(out var particleSystem)) {
    4.          gameObjectConversionSystem.AddHybridComponent(particleSystem);
    5.       }
    6.       if (TryGetComponent<ParticleSystemRenderer>(out var particleSystemRenderer)) {
    7.          gameObjectConversionSystem.AddHybridComponent(particleSystemRenderer);
    8.       }
    9.    }
    10. }
    Haven't done performance tests, but eyeballing it, it pooling doesn't seem super necessary either :)

    Screen Shot 2020-02-21 at 7.09.30 PM.png upload_2020-2-21_19-12-0.png
     
    slowdth likes this.
  11. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    I wonder there is pooling under the hood?
     
  12. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    Yes. It will sync the transform every frame (it has to).
     
  13. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    So it will pool the monobehavior instances so that when you instantiate a hybrid component it won't create a new monobehaviour particle system if it can find a previous one that has been deactivated?
     
  14. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    I would assume there is no pooling, but (so far) I haven't found it necessary.

    Exhibit A: Every one of those fireballs is a particle system instantiated using the above AddHybridComponent. (It's running on high end mobile phone at >40FPS without hiccups on instantiation). (Iirc that wasn't even batched, but only 90% sure.)
     
  15. wusticality

    wusticality

    Joined:
    Dec 15, 2016
    Posts:
    71
    Do you happen to know if there's a way to get the Prefab component onto a GameObject in-editor? I'm having to manually add it for now in a conversion behaviour.
     
  16. tommox86

    tommox86

    Joined:
    Apr 30, 2015
    Posts:
    88
    hey, how are you handling garbage collection? it seems that creating a simple particles system with the hybridcomponent causes garbage collection in the CompanionGameObjectUpdateTransformSystem. evident in builds with safety off.
     
  17. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    I've been using a pool that I adapted from AndersMalmgren - https://forum.unity.com/threads/perfomant-audiosource-pool.503056/ for my hybrid particle systems and audio clips. It's quite handy because in addition to avoiding garbage creation, it automatically recycles each particle system or audio clip when it finishes playing (by checking isEmitting and isPlaying).
     
  18. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    Anyone figure out how to do simple entity-tethered particle systems now that AddHybridComponent() has been removed in 0.50?
     
    bb8_1, Arnold_2013 and Resshin27 like this.
  19. DDKH

    DDKH

    Joined:
    Jun 13, 2013
    Posts:
    25
    Try to add them by dstManager.AddComponentObject().
    Note that it currently does support only few built-in types, like ParticleSystem, MeshRenderer, etc..
     
  20. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    Thanks, I actually already tried using dstManager.AddComponentObject().

    I found out that the problem is because i am loading prefabs with addressables in the introductory title-scene and then when i try to instantiate them in the actual game-scene, the game-objects (particle systems) for some reason get deleted in 0.50.

    So basically... it's a problem with keeping companion objects between scene changes.

    [Edit - I found an ugly hacky workaround. It sucks, but it's better than nothing]
     
    Last edited: Mar 24, 2022
  21. JesterHere

    JesterHere

    Joined:
    Dec 29, 2014
    Posts:
    10
    Any chance you could share your findings?
     
  22. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    I have a little more info in my posts here: https://forum.unity.com/threads/experimental-entities-0-50-is-available.1253394/page-2#post-7988388

    So basically, to keep the companion objects between my title-scene and the main game, I had to use LoadSceneMode.Additive and remove all game-objects in my title scene manually. It's stupid. It only really works because it's my title scene and there's not very much in it, but if I was trying to keep the prefabs between two complicated scenes it would be a nightmare.

    Entities 0.17 never had this issue. I wish there was a better workaround, but so far I have not seen any legit solutions.
     
    bb8_1 likes this.