Search Unity

Temporary Working Variables

Discussion in 'Entity Component System' started by JooleanLogic, May 16, 2018.

  1. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    Picture the following scenario. You have a bunch of ships (entities) each with a number of stacked fuel containers (entities) on board. Each ship has one fuel container that is the active one. When that container is empty, we need to select the next highest (spatial) container on the ship. In OO, the update loop looks like this.

    Code (CSharp):
    1. for each Ship
    2.     // Initialisation - These are the temporary working variables
    3.     FuelContainer highestContainer = null;
    4.     float maxY = 0;
    5.    
    6.     // Processing
    7.     for each container on ship
    8.         if(container.hasFuel() && container.position.y > maxY){
    9.             maxY = container.position.y;
    10.             highestContainer = container;
    11.         }
    12.     }
    13.    
    14.     // Post processing
    15.     if(highestContainer != null)
    16.         ship.activeContainer = highestContainer
    17. }
    In this scenario, highestContainer and maxY are temporary working variables. This works because we can iterate containers grouped by ship.
    In ECS where you're iterating on unsorted containers, where would you store such temporary variables that need to hold state related to individual ships?
    In a component on the parent Ship Entity like code below? Or perhaps in a HashMap where the Ship Entity would be the key and TempVars as the value? Is there another kind of ecs pattern for this?

    Code (CSharp):
    1. // Initialise Temp Vars
    2. for each Ship Entity
    3.     ship.TempVarsComponent.maxY = 0;
    4.     ship.TempVarsComponent.highestContainerEntity = Entity.Null;
    5. }
    6.    
    7. //Processing
    8. ComponentDataFromEntity<TempVarsComponent> shipTempVars;
    9. for each FuelContainer Entity
    10.     tempVars = shipTempVars[fuelContainer.parentEntity]; // Get temp vars from parent ship
    11.    
    12.     if(container.hasFuel() && container.position.y > tempVars.maxY){
    13.         tempVars.maxY = container.position.y;
    14.         tempVars.highestContainerEntity = container;
    15.     }
    16.     shipTempVars[fuelContainer.parentEntity] = tempVars;
    17. }
    18.  
    19. // Post processing
    20. for each Ship Entity
    21.     if(ship.tempVarsComponent.highestContainerEntity != Entity.Null)
    22.         ship.addComponent<ActiveContainerCompnent>(ship, ship.tempVarsComponent.highestContainerEntity);
    23. }
    This works well but it doesn't feel right storing such data in Components.
     
  2. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    That seems perfectly fine.

    We will add support for containers on ComponentData but organizing it the other way around like you are doing is more efficient, it avoids lots of small memory allocations for each array.
     
  3. avvie

    avvie

    Joined:
    Jan 26, 2014
    Posts:
    74
    Any possibility of the full cs file for this or a github project? I still get majorly confused with ecs. You love to read the complete file.
    Thanks
     
  4. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    340
    Takes some time to get used to but as far as I see up to now, you can store everything as components, or even their own entities. I believe it is what makes ECS beautiful, that we get to treat all of the data the same way.
     
  5. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    Thanks for the feedback.
    Most of my confusion comes from not understanding how ecs works under the hood. I was completely wrong on how I thought filtering worked. I didn't even realise it applied to shared components and not data components. :(.

    You can but I'm not sure it works well in practice. Data components are generally pretty fixed whereas working variables change all the time as you refactor code. My understanding is that component data should store the state of your game, not the local variables you use to get to that state. Local variables ideally should just exist in the Systems and between Systems.

    The crux of the problem is that I have a single logical process (engines consume fuel from the active fuel tank) split across multiple systems so I need to pass the working variables across multiple systems.

    Having said that, this was my first attempt and likely just poor design. I'm sure with a bit more thought, it could be done within a single system. Overall, I think Unity's pure implementation of ecs is going to prove to be awesome. It's just my skills that have to catch up. :)

    No you wouldn't. lol. I struggle to reason about my own code. The seemingly simple pseudo code above is very hard to follow in the actual implementation.
    My advice is to create a project with the smallest working shell of an ecs you can, and then build on it. All you need at a minimum is a ComponentData, a GameObject with that ComponentData attached, and then a ComponentSystem. That's it. Then go from there.
     
  6. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I am facing a situation that is somewhat similar to this... in the sense that there needs to be data that needs to be passed from one system to another, and it just doesn't "feel right" to make that data into a component. I am making a physics engine and I have my own "rigidbody" and "collider" components. I have a system that handles detecting contacts between colliders, and a different one that handles resolving contacts for the rigidbodies associated to those colliders

    My ContactsGenerationSystem needs to store contacts per collider entity, and my ContactsResolutionSystem needs to use those contacts later

    My plan for now (not tested yet) is that I will keep a persistent NativeMultiHashMap<Entity, Contact> somewhere publicly-accessible in my physics world, and store all contacts per collider entity in there. Later, all the systems that need to access contacts of a certain collider entity can get them from here

    But in my case, since this is an extremely performance-critical part of the system, I may end up doing something similar, but without hashmaps. Instead, I will do everything directly with conservatively pre-allocated NativeArrays, and every collider component will store the index of where their data is in those arrays
     
    Last edited: May 18, 2018
  7. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    I have a similar issue. It's one thing to have the source data be components on entities, but some best practices for passing around shared temporary processing buffers between related systems so it's easy for them to read/write to them without blowing up job read/write safety would be helpful. @Joachim_Ante - is this something in the works for Unite Berlin (or possibly before)?
     
  8. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    @PhilSA

    Would be great if you could share progress here. Not too long ago I did a pixel collision in response to a forum discussion. ECS, burst, etc where not out then. When I have some time, I plan to convert this to pure ECS. Things I need to figure out:
    - best practice for managing collision matrices - (ie Box collision -> pixel collision pairs) and accessing the from different systems - I also thought of multihashmap
    - (best) way to stop a parallel for job (ie pixel collision can stop when first hit is found)
    - parallelforbatch - any info on how this works and if it can be stopped early