Search Unity

Question Spawning Entities in job, but how to set position without creating a component?

Discussion in 'Entity Component System' started by hoesterey, Dec 22, 2020.

  1. hoesterey

    hoesterey

    Joined:
    Mar 19, 2010
    Posts:
    659
    Hi,
    I'm spawning a number of entities in a CommandBuffer. I can add a component to set the position but is there a way to "Set Values" on components when an entity is spawned this way? For example, I want to set a Owner ID on a Projectile component to track who "Shot" created me. From what I can tell you can't "SetComponent" when using a ParallelWritter though.




    Code (CSharp):
    1. public class SpawnProjectileSystem : SystemBase
    2.     {
    3.         private EndSimulationEntityCommandBufferSystem barrier;
    4.         protected override void OnCreate()
    5.         {
    6.             barrier = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    7.         }
    8.    
    9.         protected override void OnUpdate()
    10.         {
    11.             float deltaTime = Time.DeltaTime;
    12.             float elapseTime = (float)Time.ElapsedTime;
    13.             var cmdBuffer = barrier.CreateCommandBuffer().AsParallelWriter();
    14.             Entities.ForEach((Entity entity, int entityInQueryIndex, ref ProjectileSpawner spawner, ref Rotation rotation, ref Translation translation) =>
    15.             {
    16.                 if (spawner.Trigger)
    17.                 {
    18.                     spawner.CooldownCurrent = elapseTime + spawner.m_Cooldown;
    19.                
    20.                     var projectile = cmdBuffer.Instantiate(entityInQueryIndex, spawner.m_ProjectilePrefab);
    21.                     cmdBuffer.AddComponent(entityInQueryIndex, projectile, new Rotation { Value = rotation.Value });
    22. //rather set position to the exisiting transform
    23.                     cmdBuffer.AddComponent(entityInQueryIndex, projectile, new Translation { Value = translation.Value });
    24. //Really want to "Set" values on the created entities "projectile" component here. e.g.
    25.  // cmdBuffer.SetComponent(entityInQueryIndex, projectile, Projectile{ Owner = entity});  How can I do this?
    26.                     spawner.Trigger = false;
    27.                 }
    28.             }).WithName("SpawnProjectile").ScheduleParallel();
    29.             barrier.AddJobHandleForProducer(Dependency);
    30.         }
    31.     }
     
  2. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,775
    Few things here.

    Try to avoid adding components at runtime, as much as possible. Especially when comes to large number of entities.

    For that, you either use archetype, or if you use authoring GO to Entity conversion, you can add required components at conversion time. Or create new additional utility entities prefabs.

    Secondly, since you are using ECB inside job, you can use either add, or set component inside that job.

    Other options are, to spawn primaryly number of entities, using either playback, or wait a frame until entities are created, then you can use setComponent withouth ECB.

    You could also use EM, to spaw number of entities in advance.
     
  3. hoesterey

    hoesterey

    Joined:
    Mar 19, 2010
    Posts:
    659
    Thanks for the reply!

    Two follow up questions
    1) if I want to set the "Translation" components position value to be the same as the "gun that shot the projectile" with ECB would I do this?

    Code (CSharp):
    1. Entities.ForEach((Entity entity, int entityInQueryIndex, ref ProjectileSpawner spawner, ref Rotation rotation, ref Translation translation) =>
    2.   {
    3. cmdBuffer.SetComponent(entityInQueryIndex, projectile, translation);
    2) When using the command buffer, How can I set values on exiting components on the entity I created? The entities I'm created are converted gameobjects (projectiles) Each has a "Projectile" component. Let's say I want to set the "Entity IShotProjectile" to "Foo" like this:

    Code (CSharp):
    1. Entities.ForEach((Entity entity, int entityInQueryIndex, ref ProjectileSpawner spawner, ref Rotation rotation, ref Translation translation) =>
    2.             {
    3.                 if (spawner.Trigger)
    4.                 {
    5.                     spawner.CooldownCurrent = elapseTime + spawner.m_Cooldown;
    6.                     var projectile = cmdBuffer.Instantiate(entityInQueryIndex, spawner.m_ProjectilePrefab);
    7.  
    8. //unworking code.  how would I do this?
    9. cmdBuffer.SetComponentValue(projectile, Projectile{IShotProjectile = entity}
    Am I approching this wrong? Should I not be spawning my projectiles like this at all?
    Thanks again.
     
  4. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,775
    Do exactly the same way, as in your first code example, line 3.

    Then when you instantiate your projectile, you should set straight away set position and rotation, based on gun. Also you may need other properties, like velocity and dmg.
     
  5. hoesterey

    hoesterey

    Joined:
    Mar 19, 2010
    Posts:
    659
    Thanks.

    For those interested working code:

    Code (CSharp):
    1. public class SpawnProjectileSystem : SystemBase
    2.     {
    3.         private EndSimulationEntityCommandBufferSystem barrier;
    4.         protected override void OnCreate()
    5.         {
    6.             barrier = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    7.         }
    8.        
    9.         protected override void OnUpdate()
    10.         {
    11.             float deltaTime = Time.DeltaTime;
    12.             float elapseTime = (float)Time.ElapsedTime;
    13.             var cmdBuffer = barrier.CreateCommandBuffer().AsParallelWriter();
    14.             Entities.ForEach((Entity entity, int entityInQueryIndex, ref ProjectileSpawner spawner, ref ProjectileSpawnerParams spawnParams) =>
    15.             {
    16.                 if (spawner.Trigger)
    17.                 {
    18.                     spawner.CooldownCurrent = elapseTime + spawner.m_Cooldown;
    19.                     var projectile = cmdBuffer.Instantiate(entityInQueryIndex, spawner.m_ProjectilePrefab);
    20.  
    21.                     cmdBuffer.SetComponent(entityInQueryIndex, projectile, new Translation { Value = spawnParams.SpawnTranslation.Value });
    22.                     cmdBuffer.SetComponent(entityInQueryIndex, projectile, new Rotation { Value = spawnParams.SpawnRotation.Value });
    23.                     cmdBuffer.SetComponent(entityInQueryIndex, projectile, new ProjectileSpawnParams
    24.                     { ProjectileOwner = spawnParams.ProjectileOwner,
    25.                       SpawnTranslation = spawnParams.SpawnTranslation,
    26.                       SpawnRotation = spawnParams.SpawnRotation,
    27.                     });
    28.                     spawner.Trigger = false;
    29.                 }
    30.             }).WithName("SpawnProjectile").ScheduleParallel();
    31.             barrier.AddJobHandleForProducer(Dependency);
    32.         }
    33.     }