Search Unity

Best way to handle delays in ECS

Discussion in 'Entity Component System' started by Deleted User, Jul 10, 2019.

  1. Deleted User

    Deleted User

    Guest

    In the old workflow, coroutines were the best way to wait. (They even had better performance than C#'s async await).
    The question is, how we should write delayed code in ECS?
     
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,262
    Only way I can think of to do it correctly is with code gen or manual translation. Basically every coroutine can be converted into a component and a job in which all local variables of the coroutine are members of the component and then the component also has a jump index so that when the job starts it jumps to after the "yield" point and continues logic to the next "yield".

    I'm suspicious someone at Unity is trying that codegen approach to convert coroutine functions into component-job pairs, otherwise I bet someone from the community will figure it out.
     
  3. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    I think best will be just introduce a specific "Delay" component.

    Attach it to the entity which needs to be waited. (Equal to WaitForX)

    Query entities to the system only if the "Delay" component is not present on the entity.
    (In your process system, e.g. attack or whatever)

    Tick delay in a different system (simply decrement the delay value, remove component via EntityComponentBuffer)

    Process your entity as usual.

    Alternatively, you could have a system state component that contains data about the delay.
    And process it in the similar way.
     
    SenseEater and Antypodish like this.
  4. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    982
    That's a good idea. The downsize is it introduces coupling to existing systems that needs to respect the delay. Now you have to add ComponentType.Exclude<Delay>() to the queries of such systems.
     
  5. GameDeveloper1111

    GameDeveloper1111

    Joined:
    Jul 24, 2020
    Posts:
    100
    Any 2021 thoughts on this?

    I needed Function_B to be called one frame after Function_A, where each function is only ever called once. I created a system for each and organized them in the following way, enabling and disabling the systems as needed:

    System execution order:

    Frame N:
    - [DISABLED] Function_B
    - Function_A

    Frame N+1:
    - Function_B
    - [DISABLED] Function_A

    Frame N+2:
    - [DISABLED] Function_B
    - [DISABLED] Function_A

    This felt a bit contrived so I thought I'd post here in case people have come up with better solutions.
     
  6. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,769
    You could execute system B before system A.
    So when A is executed, it need wait until next frame, to execute B.
    You can also use ECB, to control when entities are executed.
     
    GameDeveloper1111 likes this.
  7. GameDeveloper1111

    GameDeveloper1111

    Joined:
    Jul 24, 2020
    Posts:
    100
    Oh, I see. Have System B look for entities with a component like
    ConsumeMeSystemBTag
    , where System A is the only thing that will add this component to an entity. Then System B can remove the component so it's not called again.

    Thanks!

    EDIT: I end up destroying these one-time initialization systems after using them, but if I end up needing them after initialization I'll switch to the more elegant entity-based toggle approach.
     
    Last edited: Feb 16, 2021