Search Unity

Looking for some ECS architecture advice for a specific case

Discussion in 'Entity Component System' started by PhilSA, Apr 11, 2018.

  1. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    Go straight to this post for a clearer explanation of the problem

    _________________________________

    I'm in the early stages of converting my networking system to ECS and Jobs, and trying to come up with a good architecture for it. Here's the specific case I'm working on: I have a MainSimulationSystem that handles reverting the world to a previous state when receiving network data, and resimulating to the present all in a single frame. Here's some pseudocode for that "MainSimulationSystem"

    Code (CSharp):
    1.  
    2. public class MainSimulationSystem
    3. {
    4.     protected void OnTick()
    5.     {
    6.         // Handle resimulation of previous ticks if necessary
    7.         if(receivedNetworkData)
    8.         {
    9.             int simulationTick = receivedTick;
    10.  
    11.             // Tell each gameplay system to revert world to received tick
    12.             foreach(IStateSystem s in allStateSystems)
    13.             {    
    14.                 s.RevertWorldToTick(simulationTick);
    15.             }
    16.  
    17.             while(simulationTick < currentTick)
    18.             {
    19.                 // Tell each gameplay system to save the world state at this tick
    20.                 foreach(IStateSystem s in allStateSystems)
    21.                 {    
    22.                     s.SaveWorldToTick(simulationTick);
    23.                 }
    24.  
    25.                 // Tell each gameplay system to simulate a frame
    26.                 foreach(ISimulationSystem s in allSimulationSystems)
    27.                 {    
    28.                     s.Simulate();
    29.                 }
    30.  
    31.                 simulationTick++;
    32.             }
    33.         }
    34.  
    35.         // Resimulation is over. Now we simulate the current frame
    36.  
    37.         // Tell each gameplay system to save the world state at present tick
    38.         foreach(IStateSystem s in allStateSystems)
    39.         {    
    40.             s.SaveWorldToTick(currentTick);
    41.         }
    42.  
    43.         // Tell each gameplay system to simulate a frame
    44.         foreach(ISimulationSystem in allSimulationSystems)
    45.         {    
    46.             s.Simulate();
    47.         }
    48.     }
    49. }
    50.  

    As you can see, all of the "ISimulationSystems" and "IStateSystems" will possibly run different kinds of updates several times within the same frame. I would like to make all ISimulationSystems and IStateSystems be JobComponentSystems. But as far as I know (and I know very little) this notion of running different kinds of updates multiple times in a frame seems to be incompatible with JobComponentSystems, which seem to only be able to have one update that automatically runs once per frame.

    Instead of relying on JobComponentSystem.OnUpdate(JobHandle inputDeps), would it be possible to just add other functions to a JobComponentSystem, and make those functions be called manually from other systems? Would it break all kinds of optimizations? What is the JobHandle that's passed as parameter to JobComponentSystem.OnUpdate(), and how can I pass it to my custom functions?

    _________________________________

    Also, don't hesitate if you can think of a better way to approach this. But right now, it seems to me like if I can't call updates on external systems manually, I'd be forced to put all the logic of my entire game inside MainSimulationSystem
     
    Last edited: Apr 12, 2018
  2. mike_acton

    mike_acton

    Unity Technologies

    Joined:
    Nov 21, 2017
    Posts:
    110
    Two ways of running systems multiple times.
    1. Update your systems manually
    2. Make your systems generic. (Generics are not updated automatically). Then derive from that a class for each instance you want to update. Control order with UpdateBefore/UpdateAfter attributes.
     
    PhilSA likes this.
  3. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    EDIT: nevermind what I just wrote, I just understood how to arrange things to make my original example work using separate worlds and "ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world);" as in the link you provided

    Thanks!

    Awesome,

    And just to be sure: is there any caveat to making a function in a regular ComponentSystem that acts like an update and that is called externally?

    Code (CSharp):
    1. public class SomeSystem : ComponentSystem
    2. {
    3.     struct Data
    4.     {
    5.         public ComponentDataArray<MyComponent> MyComponents;
    6.     }
    7.     [Inject] private Data m_MyComponents;
    8.  
    9.     public struct JobA : IJob
    10.     {
    11.         // Job that affects MyComponents....
    12.     }
    13.  
    14.     public struct JobB : IJob
    15.     {
    16.         // another job that affects MyComponents....
    17.     }
    18.  
    19.     public void DoSomething()
    20.     {
    21.         // Schedule JobA...
    22.     }
    23.  
    24.     public void DoSomethingElse()
    25.     {
    26.         // Schedule JobB...
    27.     }
    28. }
    For example if I called DoSomething() and then DoSomethingElse() on that system from another system's OnUpdate(), would it be just as good as if I had two JobComponentSystems doing those things?

    Basically I'm trying to understand if JobComponentSystems and OnUpdate() calls have special optimizations made specifically for them that I won't get if I handle jobs manually in a custom function of a regular ComponentSystem. Because even with those two options you explained, I don't know how I could make my original example work
     
    Last edited: Apr 12, 2018
  4. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    Really sorry to bring this back once again, but I am still having trouble figuring out the "correct" way to make this work afterall. I'll try to reiterate my problem more clearly and without a wall of text. Here's pseudocode for a simplified version of what I want to be able to do:

    Code (CSharp):
    1. public class MyMainManager
    2. {
    3.     // This would be called every fixed update
    4.     public void TickSimulation()
    5.     {
    6.         // [?] Tell a certain group of systems to run an update
    7.         // For example: Receive network messages, and restore the world to a past state
    8.  
    9.         // In the case where we are in an online game and have received past data,
    10.         // we might need to "resimulate" several frames of the entire game's simulation within one frame
    11.         while(haveToResimulateAFrame)
    12.         {
    13.             // [?] Tell all simulation-related systems to run an update
    14.             // For example: tick the simulation of all characters and projectiles
    15.         }
    16.  
    17.         // [?] Tell another group of systems to run an update
    18.         // For example: Handle input for local players
    19.  
    20.         // [?] Tell all simulation-related systems to run an update (the same systems we used in the resimulation loop, but this time it's for the present frame, after gathering input)
    21.         // For example: tick the simulation of all characters and projectiles
    22.     }
    23. }
    I think my problem is essentially this:
    I need to be able to put systems into groups, and then manually tell them "MySystemGroup.DoYourUpdate()". And all of the jobs of one group must be guaranteed to be finished before updating the next group. I think simply having this would solve all of my problems. Right now I'm not sure that [UpdateInGroup] and [UpdateBefore] satisfy this need, because of the part in the while loop in my example code. How would I handle telling a certain group to keep updating multiple times until a certain condition is met, and then run other groups? Would I be expected to put each of my 3 different groups in their own separate ECS Worlds, and manually tell their world to update from some kind of main loop?
     
    Last edited: Apr 12, 2018
  5. GabrieleUnity

    GabrieleUnity

    Unity Technologies

    Joined:
    Sep 4, 2012
    Posts:
    116
    I think that the specific solution for your use case would be to clone the Worlds and tick them independently.

    At the moment we don't provide an easy way to do it (but we are working on it, being cloning and merging worlds a key feature in our opinion), but you can safely move entities between Worlds using
    EntityManager.MoveEntitiesFrom
    , and you can probably start from its implementation to provide a simple way to copy chunks, before we expose an API to do it. Or just copy the entities via jobs.

    Obviously there are a million details that depend on your game: how long the history on the client should be? how many entities you have? do you need to clone all the components to be able to reply the history, or you only need a subset? For example you might just need a subset of them, and by just copying this subset, you will end up automatically restricting the systems that get invoked to only the ones that match what you need. You might tag the cloned entities to control even further which ones get processed and by which system, and so on.

    The point I'm trying to make is this: the implementation is very dependent on the way you game works, what we will expose is a set of generic and very efficient functionalities that you will be able to rely on to copy, move, filter, merge components data around.
     
  6. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    thanks for your answer,

    Could you point me in the right direction for how to trigger an update for a World? Looking at the World.cs class, I'm not seeing anything resembling an Update()

    __________________

    Also, would you have any comment on the approach suggested here?
    https://forum.unity.com/threads/val...within-a-componentsystem.526101/#post-3460756

    It seems like it would solve my problem, but will updating everything manually within the same world bypass any optimizations that I don't know of?
     
  7. GabrieleUnity

    GabrieleUnity

    Unity Technologies

    Joined:
    Sep 4, 2012
    Posts:
    116
    It depends on how your systems are implemented. What Tim's suggesting should work fine assuming that the Worlds and the ComponentSystems you are referring to are properly initialized (which is the case if you follow his code snippet's approach). I don't know what to add more to what Tim wrote.

    Once we will ship our solution, things will be more clear.
     
    FROS7 and PhilSA like this.