Suppose that I have a game engine.
Let's say it contains class Graphic, GamePlay, and Physics system.
(The real case are 20+ systems.)
All 3 of them are derived from System.
This is a draft of the simple initialization.
main(){
Game_Engine* engine = new Game_Engine();
Graphic* sys1= new Graphic(engine); //set to System::engine_pointer
GamePlay* sys2= new GamePlay(engine);
Physics* sys3= new Physics(engine);
engine->addSystem(sys1); //add to Game_Engine's hash map
engine->addSystem(sys2);
engine->addSystem(sys3);
}
Then, I want to make all system can call each other. Ex. Graphic can call GamePlay.
So I design the addSystem() as :-
class Game_Engine {
std::unordered_map<std::type_index,Sys*> hashTable;
void addSystem (System* system){
hashTable.add( std::type_index(typeid(*system)), system );
}
template <class SysXXX> SysXXX* getSystem(){
return hashTable.get(std::type_index(typeid(SysXXX)) );
}
}
The result is that each System can call each other by using only class name :-
class Graphic : public System {
void call_me_every_time_step(){
engine_pointer->getSystem<GamePlay>()->... do something ;
}
}
Now, it works as I wished, but
I heard that typeid is bad for performance.
Game_Engine.h now has to #include all Graphic.h, GamePlay.h and Physics.h, so compilation time increases.
(I tried to not include them -> typeid of 3 derived System will return wrong result.)
Is it possible to avoid those drawback? How?
Are there any other disadvantage?
Is this a bad design in the first place? If so, what is a good design?
(because I have very limited experience on C++.)
Edit 1 : Below section responses to gudok's answer
Adding a certain get/set function for each system is what I did.
However, I realized that it become harder to manage when there are more systems, at least for me.
I ran away from it and use the template code instead, as above.
For gudok's solution, a single system will increase programmer's work as followed:-
- add the field declaration in the "GameEngine"
- add another function to return a certain system
- when rename a class e.g. "Graphics" to "Render" by using automatic refactor tool, I have to rename the getGraphics() to getRender() too (to make code readable)
Comparing the code in the question, a single system cost only 1 line.
engine->addSystem(new Graphics(engine));
It is not so trivial, especially when most systems are changing name, and amount of systems are increasing constantly.
Edit 2 : Response to gudok's enhanced answer
Make the GameEngine derived from SystemHolder{T} can reduce the work per System to 2 places :-
: public SystemHolder<Graphics>
and
engine.addSystem<Graphics>(new Graphics());
It is still 2 places, though.
The code in question uses only 1 place.
Therefore, it is not good enough, but thank for trying!