Search Unity

Proper way to enable/disable systems

Discussion in 'Entity Component System' started by Chris_vf, Feb 4, 2019.

  1. Chris_vf

    Chris_vf

    Joined:
    Jul 19, 2015
    Posts:
    9
    How would you go about toggling systems depending on game context?
    Here is an example to illustrate what I mean:
    I have 3 tag components
    Selectable, Hovering, Selected

    and 2 systems
     MouseOver, MouseSelect 

    The MouseOver system casts a ray to determine if it should add or remove a Hovering component and the MouseSelect system adds a Selected component on mouse click on entities that have a Hovering component.
    Now let's say we have two modes/contexts, moving and destroying, which both have 2 reactive systems that react to adding the Selected component - in one the entity is attached to mouse pointer, in the other it is destroyed.

    Currently, I have a function per mode that toggles the relevant systems that I manually call when switching between modes. However, as I am approaching 50+ systems and various modes, this is becoming a nightmare to maintain (I haven't even considered any possible overhead from toggling systems on and off).

    What would be the appropriate way to deal with it? I've considered having a "Mode controller" entity with tag components for each mode currently on - systems that only run for X mode require that this entity has the X tag component. Any thoughts?
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    While totally possible, I think generally when designing in ECS it's advisable to not toggle your systems. Systems just react to your data. If your game is in a specific state the system should just not react just not react to it.

    There are obviously exceptions. For example, we have over 80 systems, dozens of states and never toggle systems except on the initial load screen because we like to load data asynchronously and incrementally rather than all at once. This allows us to provide a smooth loading experience as we don't want systems to react on each chunk until everything is loaded. But we don't turn systems off, we just control the initialization and only turn 1-2 systems on until loading is done then enable the rest.

    But in general for game states, change your data, not your systems.
     
  3. Chris_vf

    Chris_vf

    Joined:
    Jul 19, 2015
    Posts:
    9
    That's very helpful! Would you mind elaborating a bit? What do you think of the idea of an entity with game state tag components?

    I am actually doing something similar; how are you going about this? Using the DisableAutoCreation tag and manually creating them later?

    Sorry for the barrage of questions, I am very new to data driven programming.
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    I have a very customized world initialization including how I create them using zenject for dependency injection. Happy to share my work, just not sure how interested you'd be.

    As for what you're doing. I don't add Selected/Hovering etc to an actual entity, but modify the Selection component my 'player' entity has. This entity represents the 'player' which holds input, audio listener, selection, mouse position, current game state etc etc.

    My selection then looks like this.

    Code (CSharp):
    1.     /// <summary>
    2.     /// Component that stores the current selection, ray and next request.
    3.     /// Requires a collection of other components, see <see cref="SelectionBlueprint"/>.
    4.     /// </summary>
    5.     public struct Selection : IComponentData
    6.     {
    7.         /// <summary>
    8.         /// The request to capture the screen.
    9.         /// </summary>
    10.         public AsyncGPUReadbackRequest AsyncRequest;
    11.  
    12.         /// <summary>
    13.         /// Set when an entity is first selected.
    14.         /// When set, Over will always be the same entity.
    15.         /// Only set for a single frame until the entity exists the selection.
    16.         /// </summary>
    17.         public Entity Enter;
    18.  
    19.         /// <summary>
    20.         /// The current entity that is selected.
    21.         /// </summary>
    22.         public Entity Over;
    23.  
    24.         /// <summary>
    25.         /// When a selection is lost it is marked for a single frame as having exited.
    26.         /// </summary>
    27.         public Entity Exit;
    28.  
    29.         /// <summary>
    30.         /// The raycast hit on the <see cref="Selection.Over"/> entity.
    31.         /// </summary>
    32.         public RayHit Hit;
    33.     }
    (I don't use it, but this is a good use for the new Singleton<> api if it's a single player game.)
     
    Last edited: Feb 4, 2019
  5. Chris_vf

    Chris_vf

    Joined:
    Jul 19, 2015
    Posts:
    9
    This is very interesting and I assume much better than adding/removing components. My main problem though is how to manage game states so I can decide which systems should "work". (the selection system was just an example - I really appreciate the input though). Is having an entity representing the game state and adding/removing components per state a valid approach?
     
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    That's kind of how I do it but I won't claim it's the best, just seems right to me.

    Most entities in my world don't really care what state the game is in, it's mostly the player and how they interact with things.
     
    Chris_vf likes this.
  7. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    340
    I believe disabling systems is a code smell. Because then I am coupling the state of the system with its behavior, which goes against the reason we separated components from systems in the first place.

    If the reason is not performance, try making this state into a component and querying it in your system. This can be a little verbose now, until they add-in proper reactive systems.

    So, I say +1 from me for this approach:
    You can even distribute this state to relevant entities with SharedComponents, which will reduce the amount of queries you need.
     
    rigidbuddy and Chris_vf like this.