Skip to main content
2 of 14
added 64 characters in body
user avatar
user avatar

The way I do it, though this is not for the faint of heart as it involves variable-length structs, reinterpret casts, placement new and manual dtor invocations, and requires some knowledge of proper memory alignment if it is to be compacted as much as possible, is like this:

enter image description here

Each component type is stored in its own contiguous memory block as a VLS. There are no heap/store allocations/deallocations on a per-component instance level (they are stored contiguously in an array that grows, similar to std::vector, and you can use that here, though it needs to behave like a free list with O(1) removals from the middle without invalidating any other indices).

And a pair of indices (type + index into the container for the type) adding up to 48-bits basically links the entities and components associated to that entity together, with -1 indicating the null terminator.

And yes, there is casting going on (in my case a reinterpret cast which is even more ugly than a dynamic cast) to retrieve a component of a particular type from an entity when we write like:

MotionComponent& motion = entity.get<MotionComponent>();

But that is only one get method in the codebase that requires such a cast, and there's runtime checking going on to make sure the cast is legal to mitigate a lot of the usual problems associated with casting pointers.

user321630