We've built an entity-component architecture that works ok for our needs, but it has something that bugs me a bit from a software engineering point of view.
We use std::shared_ptr
and std::weak_ptr
for the Component
s: the Entity
owns the component through an std::shared_ptr
, while the ComponentManager
and other Component
s refer to the component through a non-owning std::weak_ptr
.
The issue is that the components are not actually shared, and they should not be shared either. They're owned by a single entity. Having an std::shared_ptr
there suggests that anyone can take a copy of the shared_ptr
and own it too.
Here is a diagram of how the classes are organized:
Entity
:
- real owner of the components through
shared_ptr
- does not know which components it holds
ComponentManager
:
- we call
update()
on theComponentManager
, which in turn callsupdate()
on every component it knows about - there is one instance per type of component; we've done it like this to have control over which components get updated first (e.g.
CmpRigidBody
updates theCmpMatrixTransform
, and from which theCmpGfx
is then updated) - unofficial owners of the components through
weak_ptr
s; it's "unofficial" because it's through them that the components are updated weak_ptr
s are stored in avector
- once the (single)
shared_ptr
goes out of scope (e.g. when the entity gets deleted), a custom deleter tells the ComponentManager to remove theweak_ptr
from thevector
(this also adds confusion to this architecture)
Component
and derived:
- holds the core of the information and performs the main operations of the component
- can reference other components through
weak_ptr
(hooked up at initialization, for now)
I strongly believe in using the right tool for the job, and although this architecture works, I'm curious to see if there are alternatives that would remove the confusion introduced by the shared_ptrs.
Maybe this is pertinent:
- The amount of entities and components is relatively low, so we won't gain much by making efforts to keep them close together (the rendering and the physics simulation are taking most of our frame time).
- We use this approach because of the re-usability offered by the components and entities from project to project.
- The project is single-threaded.
- The action takes place in the
Component
, not in theComponentManager
(i.e. it's a entity-component architecture, not an entity-component-system archiecture), so theComponent
needs access to otherComponent
s if they still exist, and these can be removed at any time.
I could use a kind of handle system through the ComponentManager
and query the appropriate one every time I need an access to a Component
, but this adds a dependency... (Maybe that's the price to pay..?!)
Do I have other options?