Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Script from ComponentSystem into SystemBase

Discussion in 'Entity Component System' started by demozbox, Nov 2, 2020.

  1. demozbox

    demozbox

    Joined:
    Nov 26, 2014
    Posts:
    83
    Hello. I need to rewrite the class of ComponentSystem into SystemBase and cirtain lines give me headache.
    In SystemBase how can I :
    - be sure if entity has component? (EntityManager.HasComponent)
    - get component from other entity? (EntityManager.GetComponentData<Translation>)
    - use method even in static class? (SharedMethods.CanAttackTarget(...))
    - use time.deltatime? (Time.DeltaTime)

    Code (CSharp):
    1. using Unity.Mathematics;
    2. using Unity.Transforms;
    3. using Unity.Entities;
    4.  
    5. public class AttackSystem : ComponentSystem
    6. {
    7.     protected override void OnUpdate()
    8.     {
    9.         Entities.WithAll<BehaviourStateAttacking>().ForEach((
    10.             Entity entity, ref Translation translation, ref Rotation rotation, ref BehaviourStateAttacking behaviourStateAttacking,
    11.             ref Turning turning, ref ShootTimer shootTimer, ref AttackDistance attackDistance
    12.             ) =>
    13.         {
    14.             float3 myPos = translation.Value;
    15.             quaternion myRot = rotation.Value;
    16.             float turningSpeed = turning.TurningSpeed;
    17.             Entity targetEntity = behaviourStateAttacking.AttackTarget;
    18.             float myAttackDistance = attackDistance.Value;
    19.  
    20.  
    21.             if (EntityManager.HasComponent(behaviourStateAttacking.AttackTarget, typeof(Translation)))
    22.             {
    23.                 // checks for attack
    24.                 float3 targetPos = World.DefaultGameObjectInjectionWorld.EntityManager.GetComponentData<Translation>(behaviourStateAttacking.AttackTarget).Value;
    25.  
    26.                 if (!SharedMethods.CanAttackTarget(myPos, targetPos, myAttackDistance))
    27.                 {
    28.                     EntityManager.RemoveComponent(entity, typeof(BehaviourStateAttacking));
    29.                     EntityManager.AddComponentData(entity, new BehaviourStateChasing { ChaseTarget = targetEntity });
    30.                 }
    31.                
    32.                 // rotation
    33.                 rotation.Value = SharedMethods.RotateTowardsTarget(myPos, myRot, targetPos, turningSpeed, Time.DeltaTime);
    34.  
    35.                 // shooting
    36.                 shootTimer.TimerCounter -= Time.DeltaTime;
    37.                 shootTimer.TimerCounter = shootTimer.TimerCounter < 0 ? 0 : shootTimer.TimerCounter;
    38.  
    39.                 if (shootTimer.TimerCounter <= 0)
    40.                 {
    41.                     shootTimer.TimerCounter = SharedMethods.MakeRandom(shootTimer.TimeRange, shootTimer.TimeRange + "timer");
    42.  
    43.                     Entity bulletEntity = EntityManager.Instantiate(BulletSpawner.instance.BulletEntityPrefab);
    44.  
    45.                     float3 bulletPos = translation.Value + math.mul(rotation.Value, new float3(0f, 0f, 1.5f));
    46.                     EntityManager.SetComponentData(bulletEntity, new Translation { Value = bulletPos });
    47.                     EntityManager.SetComponentData(bulletEntity, new Rotation { Value = rotation.Value });
    48.                 }
    49.             }
    50.             else
    51.             {
    52.                 EntityManager.RemoveComponent(entity, typeof(BehaviourStateAttacking));
    53.                 EntityManager.AddComponent(entity, typeof(BehaviourStatePatrolling));
    54.             }
    55.         });
    56.     }
    57. }
     
    astrogee likes this.
  2. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    The manual should answer your first couple questions. For Time.Delta time you assign it to a local variable and pass it into the job. Regarding static classes - Burst can only access static data if it's not mutable unless you use SharedStatic, which you probably shouldn't. Meaning your static data has to be const or readonly. Same for functions, it can only call static functions that don't touch any mutable data.
     
    demozbox likes this.
  3. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    954
    Like this:

    Code (CSharp):
    1. using Unity.Mathematics;
    2. using Unity.Transforms;
    3. using Unity.Entities;
    4.  
    5. public class AttackSystem : SystemBase
    6. {
    7.     EndSimulationEntityCommandBufferSystem barrier;
    8.  
    9.     protected override void OnCreate()
    10.     {
    11.         barrier = World.GetExistingSystem<EndSimulationEntityCommandBufferSystem>();
    12.     }
    13.     protected override void OnUpdate()
    14.     {
    15.         var dt = Time.DeltaTime;
    16.         var commandBuffer = barrier.CreateCommandBuffer();
    17.         var bulletPrefab = BulletSpawner.instance.BulletEntityPrefab;
    18.  
    19.         Entities
    20.         //.WithoutBurst() // if you need to access mainthread - uncomment this
    21.         .WithAll<BehaviourStateAttacking>()
    22.         .ForEach((Entity entity, ref Translation translation, ref Rotation rotation, ref BehaviourStateAttacking behaviourStateAttacking,
    23.             ref Turning turning, ref ShootTimer shootTimer, ref AttackDistance attackDistance
    24.             ) =>
    25.         {
    26.             float3 myPos = translation.Value;
    27.             quaternion myRot = rotation.Value;
    28.             float turningSpeed = turning.TurningSpeed;
    29.             Entity targetEntity = behaviourStateAttacking.AttackTarget;
    30.             float myAttackDistance = attackDistance.Value;
    31.  
    32.             if (HasComponent<Translation>(behaviourStateAttacking.AttackTarget))
    33.             {
    34.                 // checks for attack
    35.                 float3 targetPos = GetComponent<Translation>(behaviourStateAttacking.AttackTarget).Value; // you can use GetComponent when it's in the same world
    36.  
    37.                 if (!SharedMethods.CanAttackTarget(myPos, targetPos, myAttackDistance)) // this should be a static or public method in the system
    38.                 {
    39.                     commandBuffer.RemoveComponent<BehaviourStateAttacking>(entity);
    40.                     commandBuffer.AddComponent(entity, new BehaviourStateChasing { ChaseTarget = targetEntity });
    41.                 }
    42.  
    43.                 // rotation
    44.                 rotation.Value = SharedMethods.RotateTowardsTarget(myPos, myRot, targetPos, turningSpeed, Time.DeltaTime);
    45.  
    46.                 // shooting
    47.                 shootTimer.TimerCounter -= dt;
    48.                 shootTimer.TimerCounter = shootTimer.TimerCounter < 0 ? 0 : shootTimer.TimerCounter;
    49.  
    50.                 if (shootTimer.TimerCounter <= 0)
    51.                 {
    52.                     shootTimer.TimerCounter = SharedMethods.MakeRandom(shootTimer.TimeRange, shootTimer.TimeRange + "timer");
    53.  
    54.                     Entity bulletEntity = commandBuffer.Instantiate(bulletPrefab);
    55.  
    56.                     float3 bulletPos = translation.Value + math.mul(rotation.Value, new float3(0f, 0f, 1.5f));
    57.                     SetComponent(bulletEntity, new Translation { Value = bulletPos });
    58.                     SetComponent(bulletEntity, new Rotation { Value = rotation.Value });
    59.                 }
    60.             }
    61.             else
    62.             {
    63.                 commandBuffer.RemoveComponent<BehaviourStateAttacking>(entity);
    64.                 commandBuffer.AddComponent<BehaviourStatePatrolling>(entity);
    65.             }
    66.         }).Run();
    67.     }
    68. }
    69.  
     
    lndcobra, Onigiri and demozbox like this.
  4. demozbox

    demozbox

    Joined:
    Nov 26, 2014
    Posts:
    83
    Thank you sir. Didnt expect that whole solution.

    Thank for the link!
     
  5. demozbox

    demozbox

    Joined:
    Nov 26, 2014
    Posts:
    83
    I have rewriten the system and it works, but sometime when one bot kill the entity and third bot has that entity as turget a get the error that sais :
    ArgumentException: System.InvalidOperationException: The entity does not exist
    Thrown from job: Unity.Entities.EntityCommandBuffer._mono_to_burst_PlaybackChainChunk
    This Exception was thrown from a function compiled with Burst, which has limited exception support. Turn off burst (Jobs -> Burst -> Enable Compilation) to inspect full exceptions & stacktraces.
    EntityCommandBuffer was recorded in AttackSystem and played back in Unity.Entities.EndSimulationEntityCommandBufferSystem.

    I just dont understand the message. Need friendly explanation.
    Here is the rewriten system:
    Code (CSharp):
    1.  
    2. using Unity.Mathematics;
    3. using Unity.Transforms;
    4. using Unity.Entities;
    5.  
    6. public class AttackSystem : SystemBase
    7. {
    8.     EndSimulationEntityCommandBufferSystem barrier;
    9.  
    10.     protected override void OnCreate()
    11.     {
    12.         barrier = World.GetExistingSystem<EndSimulationEntityCommandBufferSystem>();
    13.     }
    14.     protected override void OnUpdate()
    15.     {
    16.         var deltaTime = Time.DeltaTime;
    17.         var commandBuffer = barrier.CreateCommandBuffer();
    18.         var bulletPrefab = BulletSpawner.instance.BulletEntityPrefab;
    19.  
    20.         Entities
    21.         //.WithoutBurst() // if you need to access mainthread - uncomment this
    22.         .WithAll<BehaviourStateAttacking>()
    23.         .ForEach((Entity entity, ref Rotation rotation, ref ShootTimer shootTimer,
    24.                   in AttackDistance attackDistance, in Turning turning, in BehaviourStateAttacking behaviourStateAttacking
    25.             ) =>
    26.         {
    27.             if (!HasComponent<LocalToWorld>(behaviourStateAttacking.AttackTarget))
    28.             {
    29.                 commandBuffer.RemoveComponent<BehaviourStateAttacking>(entity);
    30.                 commandBuffer.AddComponent<BehaviourStatePatrolling>(entity);
    31.             }
    32.             else
    33.             {
    34.                 // checks for attack
    35.                 LocalToWorld myTransform = GetComponent<LocalToWorld>(entity);
    36.                 float3 myPos = myTransform.Position;
    37.                 LocalToWorld targetTransform = GetComponent<LocalToWorld>(behaviourStateAttacking.AttackTarget);
    38.                 float3 targetPos = targetTransform.Position;
    39.                 float myAttackDistance = attackDistance.Value;
    40.                 float turningSpeed = turning.TurningSpeed;
    41.                 quaternion myRot = rotation.Value;
    42.  
    43.                 if (!SharedMethods.CanAttackTarget(myPos, targetPos, myAttackDistance)) // this should be a static or public method in the system
    44.                 {
    45.                     commandBuffer.RemoveComponent<BehaviourStateAttacking>(entity);
    46.                     commandBuffer.AddComponent(entity, new BehaviourStateChasing { ChaseTarget = behaviourStateAttacking.AttackTarget });
    47.                 }
    48.  
    49.                 // rotation
    50.                 rotation.Value = SharedMethods.RotateTowardsTarget(myPos, myRot, targetPos, turningSpeed, deltaTime);
    51.  
    52.                 // shooting
    53.                 shootTimer.TimerCounter -= deltaTime;
    54.                 shootTimer.TimerCounter = shootTimer.TimerCounter < 0 ? 0 : shootTimer.TimerCounter;
    55.  
    56.                 if (shootTimer.TimerCounter <= 0)
    57.                 {
    58.                     shootTimer.TimerCounter = SharedMethods.MakeRandom(shootTimer.TimeRange);
    59.  
    60.                     Entity bulletEntity = commandBuffer.Instantiate(bulletPrefab);
    61.  
    62.                     float3 bulletPos = myPos + math.mul(rotation.Value, new float3(0f, 0f, 1.5f));
    63.                     commandBuffer.SetComponent(bulletEntity, new Translation { Value = bulletPos });
    64.                     commandBuffer.SetComponent(bulletEntity, new Rotation { Value = rotation.Value });
    65.                 }
    66.             }
    67.         }).Run();
    68.     }
    69. }
     
  6. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,217
    This error happens because you may have destroyed the entity either in another system or in another EntityCommandBuffer and when this EntityCommandBuffer plays back, it is trying to add, set, or remove components to an entity that does not exist anymore. You have to be careful about the order of execution when using EntityCommandBuffers.
     
  7. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    988
    One thing you can do is to make your system always use the end of sumulation command buffer to set/add data and use only the begin simulaiton command buffer to destroy entites or remove component. That way when the simulation loop executes your ssytems it can't run on non exisitng data/entity.

    This does not solve the issue if you reference an entity in an IComponentData on another entity however.
    For instance let's say your EntityA has a Target component that reference EntityB, if EntityB gets destroyed, you need to have way to clean up that reference. Either you check for the entity's existance in every system that uses that reference or you make a system that clean up the reference set to Enitiy.null or remove the Target component.
     
  8. demozbox

    demozbox

    Joined:
    Nov 26, 2014
    Posts:
    83
    Somehow it fixed itself when I inhereited DestructionSystem from ComponentSystem to SystemBase and applied some changes to meet SystemBase requirements.
    Code (CSharp):
    1. using Unity.Collections;
    2. using Unity.Entities;
    3. using Unity.Mathematics;
    4. using Unity.Transforms;
    5.  
    6. public class DestructionSystem : SystemBase
    7. {
    8.     private EndSimulationEntityCommandBufferSystem _buffer;
    9.     private EntityQuery _group;
    10.  
    11.     protected override void OnStartRunning()
    12.     {
    13.         _buffer = World.GetExistingSystem<EndSimulationEntityCommandBufferSystem>();
    14.         _group = GetEntityQuery(ComponentType.ReadOnly<Translation>(), ComponentType.ReadOnly<Destructable>());
    15.     }
    16.  
    17.     protected override void OnUpdate()
    18.     {
    19.         var commandBuffer = _buffer.CreateCommandBuffer();
    20.  
    21.         var chunks = _group.CreateArchetypeChunkArray(Allocator.TempJob);
    22.         var destructableType = GetComponentTypeHandle<Destructable>();
    23.         var translationType = GetComponentTypeHandle<Translation>();
    24.         var entitiesType = GetEntityTypeHandle();
    25.  
    26.         Entities.WithAll<Destructor>().ForEach((Entity vilian, in Translation vilianTrans, in Destructor destructor) =>
    27.         {
    28.             var vilianPos = vilianTrans.Value;
    29.  
    30.             for (int c = 0; c < chunks.Length; c++)
    31.             {
    32.                 var chunk = chunks[c];
    33.                 var destructableTypeArray = chunk.GetNativeArray(destructableType);
    34.                 var translationTypeArray = chunk.GetNativeArray(translationType);
    35.                 var entitiesTypeArray = chunk.GetNativeArray(entitiesType);
    36.  
    37.                 for (int i = 0; i < chunk.Count; i++)
    38.                 {
    39.                     if (math.distance(vilianPos, translationTypeArray[i].Value) < 1)
    40.                     {
    41.                         commandBuffer.DestroyEntity(vilian);
    42.                         commandBuffer.DestroyEntity(entitiesTypeArray[i]);
    43.                     }
    44.                 }
    45.             }
    46.         }).Run();
    47.  
    48.         chunks.Dispose();
    49.     }
    50. }
    51.