Search Unity

Approach for updating a group multiple times per frame?

Discussion in 'Entity Component System' started by RecursiveEclipse, Nov 11, 2020.

  1. RecursiveEclipse

    RecursiveEclipse

    Joined:
    Sep 6, 2018
    Posts:
    298
    I'm looking for a good way to update all systems in a group continuously, as long as a singleton component exists. Should I just override the groups OnUpdate to something like this? The component gets removed in the same group.

    Code (CSharp):
    1. protected override void OnUpdate() {
    2.     base.OnUpdate();
    3.     while(HasSingleton<SkipTime>()) {
    4.         base.OnUpdate();
    5.     }
    6. }
    For context, this is my current design for how my "time skip" system is supposed to work.
    1. Have CurrentTime component, the current age of the simulation, not quite related to running time, as time can be skipped.
    2. Have SystemBase that maintains when it last ran, a timedelta, and the maximum time skip(like an update rate, but in large time spans). Sunlight wouldn't need to be updated as often as animal behavior.
    3. If SkipTime component exists, advance CurrentTime in steps no larger than the minimum of the update rates defined per system. Decrement SkipTime by the same amount.
    4. Keep updating the group until there is no more time remaining in SkipTime and SkipTime is removed.
     
    Last edited: Nov 11, 2020
  2. scottjdaley

    scottjdaley

    Joined:
    Aug 1, 2013
    Posts:
    163
    Prior to entities 0.16, I would have recommended using the
    UpdateCallback
    field on the
    ComponentSystemGroup
    . With this, you would return true from the callback if you wanted to continue running the systems.

    Now that 0.16 has landed, I'm less sure on the recommended way. For my own system groups, I've gone ahead and switched over to the replacement, which is to set the
    FixedRateManager
    field on the
    ComponentSystemGroup
    .

    The
    FixedRateManager
    field can be set to any instance of a IFixedRateManager. Despite its name (and likely its intended purpose), it can be used to control how many times a system should update each frame. For example, here is how I run a group exactly N times:


    Code (CSharp):
    1. public class FixedUpdateCountManager : IFixedRateManager
    2. {
    3.     private readonly int _iterations;
    4.     private int _lastIteration;
    5.    
    6.     // Needed for interface, but not used.
    7.     public float Timestep { get; set; }
    8.  
    9.     public FixedUpdateCountManager(int iterations) => _iterations = iterations;
    10.  
    11.     public bool ShouldGroupUpdate(ComponentSystemGroup group)
    12.     {
    13.         if (_lastIteration >= _iterations)
    14.         {
    15.             _lastIteration = 0;
    16.             return false;
    17.         }
    18.         _lastIteration++;
    19.         return true;
    20.     }
    21. }
    22.  
    23. public class UpdateThreeTimesGroup: ComponentSystemGroup
    24. {
    25.     public UpdateThreeTimesGroup() => FixedRateManager = new FixedUpdateCountManager(3);
    26. }
    You could modify this to make the
    ShouldUpdateGroup
    function return true until your condition is met.

    Your use case actually sounds similar to a fixed update rate, but with some time skip functionality added in. It looks like you have this state stored in singletons and on a special system, but I would consider moving this to a new
    IFixedRateManager
    implementation. The
    FixedRateCatchUpManager
    in FixedRateUtils.cs in the entities package would be a good reference since it has a similar concept of a maximum time step to jump each update iteration. The
    ShouldUpdateGroup
    function takes the
    ComponentSystemGroup
    as an argument which you can use to grab the EntityManager for the world in case you still want state on singletons.

    See https://forum.unity.com/threads/ifixedratemanager-has-me-a-little-nervous.997868/ for more discussion on the recent fixed rate changes.