Search Unity

[Help] SystemBase implenentation

Discussion in 'Entity Component System' started by georgeq, Mar 5, 2020.

  1. georgeq

    georgeq

    Joined:
    Mar 5, 2014
    Posts:
    662
    I have a set of pieces on my game, every spawned piece has a TagPiece component. One of the steps to restart the game after a game over, is to remove the pieces from the previous play. To accomplish that, I have a system which responds to the existence of a restart entity, like this.

    It works, but since this is my first project with DOTS, I'm not sure if this is the best way to implement a system like that. Any feedback will be welcome.

    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Jobs;
    3. using Unity.Burst;
    4. using Unity.Collections;
    5.  
    6. [BurstCompile]
    7. public class RestartSystem : SystemBase {
    8.  
    9.    BeginInitializationEntityCommandBufferSystem BufferSystem;
    10.  
    11.    protected override void OnCreate() {
    12.       BufferSystem = World.GetOrCreateSystem<BeginInitializationEntityCommandBufferSystem>();
    13.    }
    14.  
    15.    struct DropJob : IJobForEachWithEntity<TagPiece> {
    16.  
    17.       public EntityCommandBuffer.Concurrent Buffer;
    18.  
    19.       public void Execute(Entity entity,int index,[ReadOnly] ref TagPiece t) {
    20.          Buffer.DestroyEntity(index,entity);
    21.       }
    22.  
    23.    }
    24.  
    25.    protected override void OnUpdate() {
    26.  
    27.       Entity tagEntity = Entity.Null;
    28.       Entities.ForEach((Entity entity,ref TagRestart t) => {
    29.          tagEntity = entity;
    30.       }).Run();
    31.  
    32.       if(tagEntity!=Entity.Null) {
    33.          EntityCommandBuffer Buffer = BufferSystem.CreateCommandBuffer();
    34.          Buffer.DestroyEntity(tagEntity);
    35.          DropJob job = new DropJob() {
    36.             Buffer = Buffer.ToConcurrent()
    37.          };
    38.          JobHandle jh = job.Schedule(this);
    39.          jh.Complete();
    40.       }
    41.  
    42.    }
    43.  
    44. }
     
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,264
    1) You can convert the IJobForEachWithEntity into another Entities.ForEach.
    2) Instead of completing the job you could call BufferSystem.AddJobHandleForProducer(Dependency);
    3) We don't have enough information to know if this is truly ideal. We don't know when this system runs, how many jobs are running, or whether the TagPiece and TagEntity have children. Depending on those factors, using batch API could be significantly better.
     
  3. georgeq

    georgeq

    Joined:
    Mar 5, 2014
    Posts:
    662
    This runs after the game is over. It is triggered by a "Play Again" button on the Game Over page. So, all other (game) systems are on a "not run" state, the game is at a "full stop" state at particular moment in time. TagPiece is just a tag component attached to entities with a RenderMesh and the usual render related stuff, like Tranlation, Rotation, etc, but no children at all. The TagRestart is just a tag entity with nothing else attached. It is just a trigger to activate the system, the system then deletes this trigger so it won't run again until next time the player clicks on the "Play Again" button after loosing the game. As you may imagine, this system must finish its job before the spawn system kicks in, that's why I'm calling Complete.
     
  4. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,264
    Ok. So then you want to use EntityManager.DestroyEntity using either the EntityQuery for TagPiece (no children case) or use EntityQuery.ToEntityArray and pass that in to EntityManager.DestroyEntity if the entities have children.
     
  5. Zec_

    Zec_

    Joined:
    Feb 9, 2017
    Posts:
    148
    The [BurstCompile] attribute you have on your class can be removed, it won't do anything. The attribute is only meant for job structs and function pointers. You should attach it to your DropJob instead. The code in your Entities.ForEach will be Burst compiled by default. If you would want to use Entities.ForEach without burst you'd have to specify that with Entities.WithoutBurst().ForEach.
     
  6. BackgroundMover

    BackgroundMover

    Joined:
    May 9, 2015
    Posts:
    224
    You could save a few lines in your Update by treating TagRestart as a singleton component and putting this in your OnCreate()
    RequireSingletonForUpdate<TagRestart>()
     
  7. thebanjomatic

    thebanjomatic

    Joined:
    Nov 13, 2016
    Posts:
    36
    Combining the above suggestions, it looks like maybe it should reduce to just this:
    Code (CSharp):
    1. public class RestartSystem : SystemBase {
    2.     EntityQuery pieceQuery;
    3.  
    4.     protected override void OnCreate() {
    5.         RequireSingletonForUpdate<TagRestart>();
    6.         pieceQuery = EntityManager.CreateEntityQuery(typeof(TagPiece));
    7.     }
    8.  
    9.     protected override void OnUpdate() {
    10.         EntityManager.DestroyEntity(pieceQuery);
    11.     }
    12. }
     
    BackgroundMover likes this.
  8. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,264
    Almost. You still need to destroy TagRestart.
     
  9. georgeq

    georgeq

    Joined:
    Mar 5, 2014
    Posts:
    662
    OK... so, basically what you're saying is that; since the game is at full stop, a forced sync point won't have impact on performance and therefore is more efficient to use EntityManager directly instead of EntityCommandBuffer. Am I right?

    Understood. Thank you!

    Thank you!... The concept of singleton didn't made sense to me in this context until now.

    Thank you!... I'm going to try it!
     
  10. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,264
    Yes. That is what I am saying.
     
  11. georgeq

    georgeq

    Joined:
    Mar 5, 2014
    Posts:
    662
    Thank you very much!