Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

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:
    939
    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.