Search Unity

Proper way to find all entities with a specific component on them so I can use SetComponentData?

Discussion in 'Entity Component System' started by MostHated, Feb 8, 2019.

  1. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    Hello again everyone,
    I am trying to figure out the most effecient way to go about trying to find all the entities that have a component on them so that I can set it's data when needed. Currently, I am just trying to do it to all of them, but once I get that all straightened out, I will work on a system to do it individually based on other things, like if they are close to a waypoint, etc.

    For now, as seen below I had come across "manager.GetAllEntities();" but I feel that it must be the least efficient way to try and do this? Sure, it is fine right now while I am just testing things and only have a hand full of entities, but I imagine that down the road I won't want to be grabbing every single entity whenever I want to try and change the speed.

    Code (CSharp):
    1.  private void SpawnCars()
    2.         {
    3.             NativeArray<Entity> entities = new NativeArray<Entity>(maxVehicles, Allocator.Temp);
    4.             NativeArray<Entity> wheels = new NativeArray<Entity>(maxVehicles * 4, Allocator.Temp);
    5.  
    6.             if (spawnCars)
    7.             {
    8.                 for (int i = 0; i < maxVehicles; i++)
    9.                 {
    10.                     var randVehicle = Random.Range(0, passengerVehicle.Length);
    11.                     var randPoint = waypointSystem.GetRandomWaypoint();
    12.                     var position = spawn.transform.position; //randPoint.transform.position;
    13.                     var position2 =  spawn2.transform.position;
    14.                    
    15.                     var assembleVehicleSystem = manager.World.GetExistingManager<AssembleVehicleSystem>();
    16.                     entities[i] = assembleVehicleSystem.AssemblePassengerVehicle(passengerVehicle[randVehicle]);
    17.                     manager.AddComponentData(entities[i], new VehicleData {destinationPos = destination.transform.position, currentTurnSpeed = vehicleturnSpeed});
    18.                     manager.AddComponentData(entities[i], new MoveSpeed {speed = vehicleSpeed});
    19.                 }
    20.  
    21.                 wheels = manager.GetAllEntities();
    22.                 for (int i = 0; i < wheels.Length; i++)
    23.                 {
    24.                     if (manager.HasComponent<WheelSpin>(wheels[i]))
    25.                     {
    26.                         manager.SetComponentData(wheels[i], new WheelSpin { direction = vehicleWheelDirection , speed = vehicleWheelSpeed });
    27.                     }
    28.                 }
    29.                 entities.Dispose();
    30.                 wheels.Dispose();
    31.               }
    32.             }
    Instead of doing wheels = manager.GetAllEntities(); and then if (manager.HasComponent<WheelSpin> (wheels)), what would be the current ideal way to go about this?

    Thanks!
    -MH
     
  2. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    Replace your cycle by this:


    Code (CSharp):
    1. ForEach( (ref WheelSpin ws) =>
    2. {
    3.     ws.direction = vehicleWheelDirection;
    4.     ws.speed = vehicleWheelSpeed;
    5.  
    6. }, GetComponentGroup( typeof(WheelSpin) ) );
    or go inside and inline their code into your method to eliminate closure
     
  3. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    Hey there, I definitely appreciate the reply. I am not quite sure I follow you, though. When you say inline *their* code into mine to eliminate closure, who's code might you be referring to?
     
  4. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    Unity code that inside ForEach method.
    There is correct cycle for iterating all entities with your Component and in most inner loop it calls your delegate.

    You can copy that all cycle into your method to eliminate closure garbage :)
     
    MostHated likes this.
  5. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,775
    You can use groups, to filter and fetch entities, with desired components sets. Useful if you have many components to filter, or need multiple groups of entities per system.
     
  6. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    It certainly looks much more concise. I have only been using ECS for 2-3 days, so I am trying to learn all of these little tips and tricks to try and maximize my performance with it so as not to gain bad habits right off the bat. So I thank you much for that! I will give it a go.

    I have been seeing different things about that, such as componentgroup, archetypesomething, archetypesomethingelse, though I have been a bit confused on the circumstances in which they would be used as the examples I keep seeing, for anything archetype related at least looked like you would be creating something from scratch as opposed to finding/getting something that already existed?

    What it looked like was you would use something like:

    Code (CSharp):
    1. EntityArchetype entityArchetype = EntityManager.CreateArchetype(typeof(SomeComponent), typeof(SomeOtherComponent));
    and it seemed like when you did that, the created entityArchetype would be an instantiated entity in which you would then have SomeComponent and SomeOtherComponent on it because I remember hearing that every entity with a component on it *is* always an archetype, it just changes what type it is if it's components change. So then in the above, is entityArchetype not actually an entity with those two components on it?

    There was similar confusion with ComponentGroups as well. Mostly just not knowing exactly what it was for or how it was used though. Obviously it's a group of components, but how to populate it and what exactly it could / should be used for is definitely what I am trying to find out though. : D
     
  7. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    A separate but related question is, is using this below the best (or only?) way to go about updating the values of an entity?
    Code (CSharp):
    1.  manager.SetComponentData(wheels[i], new WheelSpin { direction = vehicleWheelDirection , speed = vehicleWheelSpeed });
    2.  
    I have another component called VehicleData in which I am storing destination, distance to destination, etc for my waypoint system I am working on converting as well. I was testing with only a single Entity, and I created a few buttons just to test out switching from one waypoint to another.

    Code (CSharp):
    1.     public void AssignWaypoint(GameObject destination)
    2.         {
    3.             NativeArray<Entity> entities = new NativeArray<Entity>(maxVehicles, Allocator.Temp);
    4.             entities = manager.GetAllEntities();
    5.             for (int i = 0; i < entities.Length; i++)
    6.             {
    7.                 if (manager.HasComponent<VehicleData>(entities[i]))
    8.                 {
    9.                     manager.SetComponentData(entities[i], new VehicleData { destinationPos = destination.transform.position, currentTurnSpeed = vehicleturnSpeed});
    10.                 }
    11.             }
    12.             entities.Dispose();
    13.         }
    This was the only way I could think of at the time, building on my first question about getting the entities properly by componenttype, but is there a better way to actually change/assign values to the icompentdata without wiping it out with a new one? I would imagine that I would have to repopulate all the values each time instead of just updating the specific one I wanted.
     
  8. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,775
    You can create entity with predefined archetype.
    This allows efficiently create bunch of entities rapidly.
    But then, nothing stops, from adding and removing extra components.
    So if initially you have only 2 components from archetype, you can later add another 10 if you like.
    For example position, rotation, scale, color, health, cost, faction etc., etc.

    Now you can use IJobProcessComponentData, with up to 6 components (> latest unity 2019?), otherwise 4 componetns, and execute your system that way.

    Or you can use any IJob, based on groups.
    For example creating ComponentGroup in CreateManager
    Code (CSharp):
    1. protected override void OnCreateManager ( )
    2.         {      
    3.  
    4.             group = GetComponentGroup
    5.             (        
    6.                 typeof ( Position )
    7.             ) ;
    8. }
    And your OnUpdate / Job execute any entities, matchng that group.

    Or different group example
    Code (CSharp):
    1. group_raycastPointer = GetComponentGroup
    2.             (
    3.                 typeof ( RaycastPointerTag ),
    4.                 typeof ( RaycastHitPointDistanceData ),
    5.                 typeof ( AABBHitPointNormalData ),
    6.                 typeof ( RaycastInstanceData ),                        
    7.                 typeof ( RaycastInstancesBufferElement ),
    8.                 typeof ( Position )
    9.             ) ;
    Etc.
    You can have multiple groups per system of course.

    In on update for example you can get Position

    Code (CSharp):
    1. ComponentDataFromEntity <Position> positionData = GetComponentDataFromEntity <Position> () ;
    Further you can pass positionData into job, where you get/set actual position component like

    Code (CSharp):
    1. Position position = positionData [entityWithPostion] ;
    In same manner you can access other components.
    You don't need manager or EntityCommandBuffer for that.

    These are only examples, and not the only solutions.
     
  9. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    Ok?
     
  10. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    Great info, I appreciate it. When you do GetComponentGroup that creates the group, then you can just do something like var entities = componentGroupName.GetEntities or whatever it might be and then you can work on it as I had above?

    I tried looking for stuff like that, but I think part of my issue was in my examples above, all of that was technically done in a Monobehavior item which was used as a setup tool of sorts. It looks like I was just not able to see/try any of those types of things because of that. >_< The GetAllEntities() I had used was the only thing I could seem to find, but that would explain why, lol.
     
  11. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,775
    Yes. Your var in this case will be EntityArray.

    Switching between OOP (MonoBehaviour) and ECS (Jobs) paradigm may be initially confusing, but I think, it don't take much time, to adjust. But is indeed, another quite steep learning curve. Dynamics of evolving ECS does not help in that :p
     
  12. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    Yeah, lol. The first tutorial I had done on it, the next video was "nevermind about everything we just did, its changed now. Now we do it this way!" lol. Luckily so far the things I see that have/are changing are all for the better and ease of use. So nothing wrong with that.
     
    Antypodish likes this.