Hacker News new | ask | show | jobs
by geneotech 1094 days ago
Thank you! The approach you describe is indeed the "final form" of a brutally data-oriented ECS, but my method is a bit simpler - e.g. if the game has:

struct player { components::transform transform; components::health health; };

struct box { components::transform transform; components::physics physics; };

Then my ECS simply holds a std::vector<player> and a std::vector<box> rather than having a vector per each type of component. I also use the integer identifiers like you describe but with a twist - there is one more indirection involved so that deleting from the vectors does not invalidate existing indices pointing to vector elements. It's still exactly O(1). See the section on the Memory pool in the game repo's README.

3 comments

This is pretty much how games outside of general purpose engines have always done it. With some exciting variations like having one monster struct that has every property for every entity in the game so you just have a single array of entities. Or having each struct contain an ID field so you can pass around pointers and cast them based on the ID value.
Indeed, such a rigid struct-based "ECS" is basically how most non-ECS games do it, but using components to compose game objects, if you say put them into std::tuple, makes it easy to to later call e.g. a generic lambda only on vectors of objects that contain given components. This makes for an excellent statically polymorphic code that doesn't care about what particular type the game object might have (is it a character? is it a tree?) as long as it has e.g. physics + health. In my 2013 article I also describe how to apply the same concept if we don't hardcode a specific collection of "structs of components", but add/remove components to entities at runtime.
Yeah this is the nice thing about writing a game without a general purpose game engine, you only need to go as deep into these things as the game needs. Spend a lot of time with general purpose engines and it’s always a bit of a pain when their world representation is a little bit ‘wrong’ for your game.
Columnar stores were in use in games since at least the mid-90s.

Very common way of optimizing for CPU caches.

you can avoid invalidating indices by using a sparse array (like a flat_map) instead of a vector