Rust (Bevy): ECS Network data structure

990 Views Asked by At

I am not quite sure if I completely understand the Entity Component System approach, and probably that is one of the reasons for the emergence of this question. Still struggling with the OOP mindset!

I am trying to create a data structure similar to a network, so for instance, something like a circuit:

Circuit.

So, for instance, entity 4 is connected to 1, 1 to 2, and so on. So far I have understood how to create the components, but I can't understand how the connectivity information should be stored. I believe that the entity should point to another entity?!? I have also imagined that would be even better practice to have a component that would have the connectivity information, but in that case, once again, what should it store? Ideally would be the entities themself, right? How to do it?

1

There are 1 best solutions below

3
On

You should always refer to other entities using their Id and don't store pointers to them between System runs.

Entity id in Bevy stored in struct Entity Note:

Lightweight unique ID of an entity.

Also, if this network is something unique for your game (e.g. there are maximum one instance in whole game) than it's connectivity data can be stored in the "resource". You get most benefits from Entity-Component part of ECS when your entities and components are created and processed in large quantities.

I cannot say anything more about your particular use-case because I don't know how many networks there would be, how you plan use them and interact with them. ECS is DDD pattern (Data-Driven Development) so architecture depends on how much data exists, which properties this data has and how it is used.

Example of multiple networks

If you have multiple networks, you can store them in such fashion:

#[derive(Component)]
struct NetworkTag; // Any entity with this tag is network.

// Spawn network into world like this
let network_id: Entity = world.spawn()
     .insert(NetworkTag)
     .id();

And you can store parts of electric network in such fashion:

#[derive(Component)]
struct PlusConnections(Vec<Entity>);

#[derive(Component)]
struct NetworkId(Entity);

// Spawn part into world like this
let part_id = world.spawn()
       .insert(NetworkId(network_id))
       .insert(PlusConnections(other_part_ids));

After finishing spawning parts like that, you would know only connections on plus side but you can fill negative side in separate system, for example:

#[derive(Component)]
struct NegConnections(Vec<Entity>);

fn fill_negatives_system(
   query_el_parts: Query<(Entity, &PlusConnections)>,
   query_el_parts_wo_neg: Query<Entity, Without<NegConnections>>,
   mut commands: Commands
){
   let mut positive_to_negative: HashMap<Entity, Vec<Entity>> = HashMap::new();
   for (neg, pc) in query_el_parts.iter(){
       for &positivein pc.0.iter(){
          positive_to_negative.entry(positive).or_default().push(neg);
       }
   }
   for (pos, negatives) in positive_to_negative{
      commands.entity(pos).insert(NegConnections(negatives));
   }
   // At the next tick, all nodes in electric grid would know what is their negative and positive connections and in which networks they are stored.
}