Search Unity

Question Is it possible to assign a companion game object at runtime after conversion?

Discussion in 'Entity Component System' started by HeyZoos, Jul 7, 2021.

  1. HeyZoos

    HeyZoos

    Joined:
    Jul 31, 2016
    Posts:
    50
    I have a multiplayer game with data representing units sync'd between both the client and the server. I'd like the client-side version of the data to be associated with a companion game object responsible for "presenting" the data in the form of an animated model, particle system, etc.

    Is there any way to do something like this?


    Code (CSharp):
    1.     [ClientWorld]
    2.     public class UnitPresentationSystem : SystemBase
    3.     {
    4.         private EndSimulationEntityCommandBufferSystem _endSimulationEcbSystem;
    5.  
    6.         protected override void OnCreate()
    7.         {
    8.             base.OnCreate();
    9.             _endSimulationEcbSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    10.         }
    11.  
    12.         protected override void OnUpdate()
    13.         {
    14.             var ecb = _endSimulationEcbSystem.CreateCommandBuffer();
    15.  
    16.             Entities.WithAll<NeedsPresentationTag>()
    17.                 .ForEach((Entity entity, in UnitTag unitData, in Translation translation, in LocalToWorld localToWorld,
    18.                     in Rotation rotation) =>
    19.                 {
    20.                     var conversionSystem = World.GetExistingSystem<GameObjectConversionSystem>();
    21.  
    22.  
    23.                       // This is the part I'm making up / would like to know how to do
    24.                     conversionSystem.AddCompanionGameObject(entity, UnitPresentationManager.GetPresentationGameObjectFromPool());
    25.  
    26.  
    27.                     ecb.RemoveComponent<NeedsPresentationTag>(entity);
    28.                 })
    29.                 .WithoutBurst()
    30.                 .Run();
    31.         }
    32.     }
    When a unit is spawned, I want to grab a presentation game object from the pool and assign it to the entity. I'll probably return the game object to the pool after the entity is destroyed.
     
  2. HeyZoos

    HeyZoos

    Joined:
    Jul 31, 2016
    Posts:
    50
    Here's the current / horrible way of doing it. I'm manually syncing the position and rotation to the gameObject using a dictionary. As you might expect it runs incredibly badly

    Code (CSharp):
    1.         protected override void OnUpdate()
    2.         {
    3.             // Acquire an ECB for propagating changes to the entity at the end of the frame.
    4.             var ecb = _endSimulationEcbSystem.CreateCommandBuffer();
    5.  
    6.             Entities.WithAll<NeedsPresentationTag>()
    7.                 .ForEach((Entity entity, in UnitTag unitData, in Translation translation, in LocalToWorld localToWorld,
    8.                     in Rotation rotation) =>
    9.                 {
    10.                     UnitPresentationManager.Present(entity, translation.Value, rotation.Value);
    11.                     ecb.RemoveComponent<NeedsPresentationTag>(entity);
    12.                 })
    13.                 .WithoutBurst()
    14.                 .Run();
    15.  
    16.             Entities.WithNone<NeedsPresentationTag>()
    17.                 .ForEach((Entity entity, in UnitTag unitData, in Translation translation, in LocalToWorld localToWorld,
    18.                     in Rotation rotation) =>
    19.                 {
    20.                     var transform = UnitPresentationManager.Singleton.GameObjects[entity].transform;
    21.                     transform.position = translation.Value;
    22.                     transform.rotation = rotation.Value;
    23.                 })
    24.                 .WithoutBurst()
    25.                 .Run();
    26.         }
     
  3. HeyZoos

    HeyZoos

    Joined:
    Jul 31, 2016
    Posts:
    50
    I'd like to also clarify that I tried using `AddComponentObject` and couldn't quite get it to sync the transforms.

    Code (CSharp):
    1.             Entities.WithStructuralChanges().WithAll<NeedsPresentationTag>()
    2.                 .ForEach((Entity entity, in UnitTag unitData, in Translation translation, in LocalToWorld localToWorld,
    3.                     in Rotation rotation) =>
    4.                 {
    5.                     EntityManager.AddComponentObject(entity, UnitPresentationManager.Present());
    6.                     EntityManager.AddComponentData(entity, new CopyTransformToGameObject());
    7.                     ecb.RemoveComponent<NeedsPresentationTag>(entity);
    8.                 })
    9.                 .WithoutBurst()
    10.                 .Run();
     
  4. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    992
    I understand that you want to avoid having the presentation part on server side.
    Have you tried the other way around ? Leaving the presentation part on the prefab and removing it only on server. Maybe you could even have a server init system that t changes the prefab entities themselves to avoid doing it for each instantiation.