How do entity indexes work?

So I'm trying to create different entities for my game. I was thinking I can just store them, a struct with fields, into a Vec. And than I can iterate over the Vec to read every structs fields, like position, and adjust this where needed.
However, particularly from this talk: game ECS, they use an Entity ID. I don't really follow how a number will do anything, since I will need to read its fields and use them.

Is my way of setting and storing the entities not good? What am I missing with using entity IDs?

1 Like

The problem with Vec indices is that they can change: Whenever an entity is removed from the vector, all of the subsequent ones move up one position to fill the empty space¹. By using an ID that isn't quite so directly tied to how the entity is stored, you don't have to worry about the id changing as the entity is moved around in storage.

If your entities don't need to ever directly refer to each other, this won't be an issue you face. Most games, however, will want to have some way to create and store these references. A full Entity-Component system, which might be overkill, stores all of the information relevant to a single subsystem together and then uses common entity ids to keep track of how they relate to each other.

For example, everything that gets drawn as a sprite might have an entry in a sprites table used by the graphics system. And everything that moves around could have an entry in an objects table used by the physics system. In order to draw a moving object's sprite at the right location, there needs to be some way to tell which row in objects corresponds to a particular row in sprites, and that's where something like an entity ID becomes really necessary instead of just a convenience.

¹ Or the last one is swapped into the now-empty place, but that also has the same problem.

3 Likes

So am I right in understanding that components are linked to IDs, and the IDs are linked to the Entities?

That's basically right, but a lot of ECSs don't materialize entities at all. Instead, an entity is defined implicitly by the component tables its ID appears in.

2 Likes

Oh I see. Thanks it makes a lot of sense now!

Side question. Is it good to make (probably a lot of) separate global types as components, f.e. type HealthComponent: i32;, and link these to the IDs? Or should they be structs? Since I see this is what is being done in the talk as well:

// Just for symmetry, let's let Health be a struct type
struct Health(f32);

I don't fully see why either should/or can be used, it's just that type seems simpler.

It's not clear what you mean here, but semantically an ECS is storing a foo: Map<EntityId, Foo> for every component type Foo, you only get one component of that type for each entity. You pretty much always you want a component type for every different component... type.

Of course, in practice the storage gets a lot fancier than that for performance, but the idea is basically the same.

1 Like

There's an alternative semantic interpretation where a "component" is the map itself instead of the individual record type:

trait Component {
    type Data;
    fn process_frame(&mut self);
    fn get(&self, _:EntityId)->Option<Self::Data>;
    fn set(&mut self, _:EntityId, _:Option<Self::Data>);
    // etc...
}