I have a scene, in the scene there are multiple systems. Transformsystem, collidableSystem etc ... Currently, these do nothing except returning a bool if the parameter component matches the system:
bool DrawableSystem::match(Component* component)
{
return dynamic_cast<Drawable*>(component) != 0;
}
The parent class handles everything else:
class System
{
protected:
std::vector<Component*> _components;
std::map<int, int> _idToOffset;
public:
System();
~System();
void initiate();
void update(long);
void remove(unsigned int);
Component& getComponent(unsigned int);
bool contains(unsigned int);
void add(Component*, unsigned int);
virtual bool match(Component* component) = 0;
};
The match method is used in scene when i for example instantiate an entity (I dont really have an entity class, its just a vector of components). It returns the id which is used when interacting with the components representing that entity.
unsigned int Scene::instantiate(std::vector<Component*> components)
{
_currentId++;
for (Component* component : components) {
for (System* system : _systems) {
if (system->match(component)) {
system->add(component, _currentId);
break;
}
}
}
return _currentId;
}
This will make it so that the vector _components in each system consists of only components associated with that system.
The current design is very maintainable and extendable, i don't have to modify any code in scene when adding a new type of component or system as you can see:
Scene::Scene() {
_currentId = -1;
_systems.push_back(new TransformSystem());
_systems.push_back(new DrawableSystem());
_systems.push_back(new CollidableSystem());
_systems.push_back(new ScriptableSystem(*this));
}
Scene::~Scene()
{
for (System* system : _systems) {
delete system;
}
}
void Scene::initiate()
{
for (System* system : _systems) {
system->initiate();
}
}
void Scene::update(long dt)
{
for (System* system : _systems) {
system->update(dt);
}
}
unsigned int Scene::instantiate(std::vector<Component*> components)
{
_currentId++;
for (Component* component : components) {
for (System* system : _systems) {
if (system->match(component)) {
system->add(component, _currentId);
break;
}
}
}
return _currentId;
}
void Scene::addComponent(Component* component, unsigned int id)
{
for (System* system : _systems) {
if (system->match(component)) {
system->add(component, id);
}
}
}
bool Scene::exists(unsigned id)
{
for (System* system : _systems) {
if (system->contains(id)) {
return true;
}
}
return false;
}
void Scene::destroy(unsigned int id)
{
for (System* system : _systems) {
if (system->contains(id)) {
system->remove(id);
}
}
}
But from what i've heard oop should not be used extensively like i do when designing a game engine.
I tried a purely data oriented design in the beginning but it started to get difficult to modify when i got up to around 4 different systems / component types.
Worth mentioning is that this is my first time making a game engine or anything remotely that complex so i can't really plan everything out in detail and just implement it by looking at an UML diagram. I constantly come up with better architectural choices for how the systems / scene communicate, additional method that could be useful etc ...
Do you gain so much in performance using a data oriented approach that reducing maintainability / modifiability would be worth it? It does not feel like it at all in my case, but i'd really like to learn how they do it in the industry and start learning "the right way" from the start. Are there other potantial gains with dod that i haven't thought about?