Search Unity

[Hybrid ECS] Best way to handle Convert and Inject Game Object Prefab?

Discussion in 'Entity Component System' started by desertGhost_, Aug 29, 2019.

  1. desertGhost_

    desertGhost_

    Joined:
    Apr 12, 2018
    Posts:
    260
    Hi,

    I find the process for instantiating a pure entity from a prefab to be very straightforward (like in this example), but how should I go about instantiating a hybrid entity (a Convert and Inject Game Object Conversion)?

    Should I just leave ConvertToEntity on the prefab and instantiate with Object.Instantiate() in a ComponentSystem since I still need to access the Game Object?

    Is there a more performant way to do this (in a similar way to a pure entity) so that I can instantiate my Hybrid ECS prefabs in a JobComponentSystem perhaps?

    Thanks.
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Probably the best way.

    I assume you mean in a job - not really.

    I think the most performant way is you can pool the gameobjects by creating a bunch at runtime and just disabling them (which is very common for classical Unity development). Then when you create an entity simply have a system dedicated to assigning the entity to a gameobject as required.

    This way you can create your entities just like pure entities and still have them linked to a gameobject for hybrid behaviour. If you need an example look at the LightSystem it does something similar.
     
  3. desertGhost_

    desertGhost_

    Joined:
    Apr 12, 2018
    Posts:
    260
    I've used object pooling quite a bit in monobehavior land, but I've mainly reserved it for things that get spawned relatively frequently like projectiles or impact marks. That being said, I think object pooling is the most efficient approach and I'll give it a try. I'll create two prefabs for each hybrid ecs object: 1 of the gameobject, 1 of the entity that controls the gameobject. Then I'll use a system like you suggested to link the entity to a gameobject in the object pool. I'll try having this system connect the entity to the gameobject's components (with EntityManager.AddComponentObject()) similarly to how the convert to entity script does it.

    Thanks.
     
  4. iamfletch

    iamfletch

    Joined:
    Feb 23, 2017
    Posts:
    3
    Hi,

    Did you find a good way to get the reference of the GameObject inside the ComponentSystem? I've been playing a few ideas and nothing seems to work well.

    Thanks,
    Fletch
     
  5. desertGhost_

    desertGhost_

    Joined:
    Apr 12, 2018
    Posts:
    260
    Yes. I ended implementing an object pool for GameObjects that uses the Addressables system to spawn GameObjects at runtime. I created a component type that I add to any hybrid entity:

    Code (CSharp):
    1.     [System.Serializable]
    2.     struct Hybrid : IComponentData
    3.     {
    4.         public NativeString512 address;
    5.     }
    This component has the address of the GameObject's prefab in the project.

    A hybrid object pool system loads the prefab, instantiates it and links the new gameobject with the entity that requested it.

    I link each component of the gameobject to the entity like this:
    EntityManager.AddComponentObject(entity, component);


    Now I can access the gameobject's components in any ComponentSystem or SystemBase (when using WithoutBurst and Run) systems.

    I have a separate system that makes the transform follow the entity.

    I use an object pool for GameObject spawning performance reasons, but if you don't need to respawn GameObjects very often, then your system could simply:

    1. Use Addressables to instantiate a gameobject for an entity with a hybrid component.
    2. Link the instantiated gameobject's components to the entity.
    3. Add a system state variable to the entity (so your system doesn't spawn another gameobject for the entity each frame).
    4. Cleanup gameobjects for destroyed hybrid entities (using said system state variable).
     
  6. iamfletch

    iamfletch

    Joined:
    Feb 23, 2017
    Posts:
    3
    @desertGhost_ thanks for the amazing reply. I ended up going a completely differen't route because performance isn't really an issue for me. I only use hybrid for the player model because its from the asset store and it makes life easier. I have added in how I achieved it below.

    For anyone interested I was following this guide (almost verbatum):
    https://docs.unity3d.com/Packages/com.unity.netcode@0.0/manual/getting-started.html

    I then created an empty game object in the main scene, added the following component and then referenced the prefab.
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Prefabs : MonoBehaviour
    4. {
    5.     private static Prefabs m_Instance;
    6.  
    7.     public static GameObject PlayerModel => m_Instance.playerModel;
    8.  
    9.     public GameObject playerModel;
    10.  
    11.     private void Awake()
    12.     {
    13.         if (m_Instance == null)
    14.         {
    15.             m_Instance = this;
    16.         }
    17.         else
    18.         {
    19.             Debug.LogError("Prefab Singleton loaded twice");
    20.         }
    21.     }
    22. }
    After that I added this system to "add" it to the entity.
    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.NetCode;
    3. using Unity.Transforms;
    4. using UnityEngine;
    5.  
    6. [UpdateInGroup(typeof(ClientSimulationSystemGroup))]
    7.  
    8. public class PlayerModelSystem : ComponentSystem
    9. {
    10.     protected override void OnUpdate()
    11.     {
    12.         Entities.WithNone<Transform>().ForEach((Entity playerEntity, ref PlayerComponent playerComponent) => {
    13.             if (Prefabs.PlayerModel != null)
    14.             {
    15.                 var playerModel = Object.Instantiate(Prefabs.PlayerModel);
    16.                 EntityManager.AddComponentObject(playerEntity, playerModel.GetComponent<Transform>());
    17.                 EntityManager.AddComponentData(playerEntity, new CopyTransformToGameObject());
    18.             }
    19.         });
    20.     }
    21. }
     
    Last edited: Mar 15, 2020
    hugokostic, Dr_SuiuS and desertGhost_ like this.