假设我有一个游戏引擎。
假设它包含类图形, GamePlay 和物理系统。
(真实案例是20多个系统。)
所有这3个都来自系统。
这是简单初始化的草稿。
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);
}
然后,我想让所有系统都可以互相调用。 防爆。 图片可以调用 GamePlay 。
所以我将 addSystem()设计为: -
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)) );
}
}
结果是每个系统可以仅使用类名称相互调用: -
class Graphic : public System {
void call_me_every_time_step(){
engine_pointer->getSystem<GamePlay>()->... do something ;
}
}
现在,它按照我的意愿行事,但
我听说typeid对性能不利。
Game_Engine.h 现在必须#include所有 Graphic.h , GamePlay.h 和 Physics.h ,因此编译时间增加 (我试图不包括它们 - &gt; 3个派生系统的typeid将返回错误的结果。)
是否可以避免这些缺点?怎么样?
还有其他缺点吗?
这首先是一个糟糕的设计吗?如果是这样,什么是好的设计?
(因为我对C ++的经验非常有限。)
编辑1:下面对gudok的答案的回复
我为每个系统添加了一个获取/设置功能。
但是,我意识到,当有更多系统时,管理变得更加困难,至少对我而言。
我逃避了它并使用模板代码,如上所述。
对于gudok的解决方案,单个系统将增加程序员的工作如下: -
比较问题中的代码,单个系统只需1行。
engine->addSystem(new Graphics(engine));
它并非如此简单,特别是当大多数系统正在改变名称时,系统的数量不断增加。
编辑2:对gudok增强答案的回应
从 SystemHolder {T} 派生的 GameEngine 可以将每个系统的工作量减少到2个位置: -
: public SystemHolder<Graphics>
和
engine.addSystem<Graphics>(new Graphics());
但是,它仍然是2个地方
有问题的代码仅使用1个地方
因此,这还不够好,但感谢您的努力!
答案 0 :(得分:1)
使用哈希映射和typeids而不是在GameEngine中分别存储每个系统的原因是什么?在语义上,所有这些系统都做不同的事情。我宁愿做以下事情:
class GameEngine {
std::vector<System*> systems;
Graphics* graphics;
Gameplay* gameplay;
Physics* physics;
void setGraphics(Graphics* graphics) {
this->graphics = graphics;
this->systems.push_back(graphics);
}
Graphics* getGraphics() {
return this->graphics;
}
...
};
这个解决方案背后的想法是:
Graphics
的函数,而不是所有System
的通用函数。分别存储每个系统会消除typeid和不必要的类型转换的必要性。如果您需要以统一的方式处理所有系统(例如,推进游戏时间),请使用systems
字段:
for (auto it = systems.begin(); it != systems.end(); it++) {
it->tick();
}
编辑这是增强的解决方案。您可以通过从GameEngine
继承SystemHodler
来添加新系统。根据需要,使用System
和getSystem<T>
方法获取和设置特定setSystem<T>
的实例是统一的。
#include <vector>
class System {
public:
virtual ~System() {}
};
class Graphics : public System {};
class Physics: public System {};
template<typename T>
class SystemHolder {
public:
T* getSystem() { return system; }
void setSystem(T* system) { this->system = system; }
private:
T* system;
};
class GameEngine: public SystemHolder<Physics>, public SystemHolder<Graphics> {
public:
template<typename T>
inline void addSystem(T* system) {
systems.push_back(system);
SystemHolder<T>::setSystem(system);
}
template<typename T>
inline T* getSystem() {
return SystemHolder<T>::getSystem();
}
private:
std::vector<System*> systems;
};
int main(int argc, char* argv[]) {
GameEngine engine;
engine.addSystem<Physics>(new Physics());
engine.addSystem<Graphics>(new Graphics());
engine.getSystem<Physics>();
engine.getSystem<Graphics>();
}