因此,我试图考虑一个游戏如何处理所有实体的更新,我有点困惑(可能是因为我以错误的方式进行)
所以我决定在这里发布这个问题,向您展示我目前的思考方式,看看有没有人可以向我提出一个更好的方法.
目前,我有一个CEngine类,它指向需要的其他类(例如CWindow类,CEntityManager类等)
我有一个游戏循环,伪代码会像这样(在CEngine类内)
while(isRunning) { Window->clear_screen(); EntityManager->draw(); Window->flip_screen(); // Cap FPS }
我的CEntityManager类看起来像这样:
enum { PLAYER,ENEMY,ALLY }; class CEntityManager { public: void create_entity(int entityType); // PLAYER,ALLY etc. void delete_entity(int entityID); private: std::vector<CEntity*> entityVector; std::vector<CEntity*> entityVectorIter; };
我的CEntity类看起来像这样:
class CEntity() { public: virtual void draw() = 0; void set_id(int nextEntityID); int get_id(); int get_type(); private: static nextEntityID; int entityID; int entityType; };
之后,我将为敌人创建一个类,并给它一个精灵表,它自己的功能等.
例如:
class CEnemy : public CEntity { public: void draw(); // Implement draw(); void do_ai_stuff(); }; class CPlayer : public CEntity { public: void draw(); // Implement draw(); void handle_input(); };
所有这一切都很好,只是画精灵到屏幕上.
但是,我来到了使用一个实体中存在但不在另一个实体中的函数的问题.
在上面的伪代码示例中,do_ai_stuff();和handle_input();
从我的游戏循环中可以看到,有一个调用EntityManager-> draw();
这只是迭代通过entityVector并调用draw();每个实体的功能 – 所有实体都有draw();功能.
但是后来我想,如果是一个需要处理输入的玩家实体呢?
这是如何工作的?
我没有尝试过,但我认为我不能像我在draw()函数中循环一样,因为像敌人这样的实体不会有一个handle_input()函数.
我可以使用if语句来检查entityType,像这样:
for(entityVectorIter = entityVector.begin(); entityVectorIter != entityVector.end(); entityVectorIter++) { if((*entityVectorIter)->get_type() == PLAYER) { (*entityVectorIter)->handle_input(); } }
但是我不知道人们通常会如何写这个东西,所以我不知道最好的方法.
我在这里写了很多,我没有提出任何具体的问题,所以我会澄清我在这里寻找什么:
>我是否已经布局/设计了我的代码好吗?它是否实用?
>我有更好的更有效的方法来更新我的实体和调用其他实体可能没有的函数?
>使用枚举来跟踪实体类型是识别实体的好方法吗?
解决方法
通常你会看到这样的东西
class CEntity { public: virtual void draw() {}; // default implementations do nothing virtual void update() {} ; virtual void handleinput( const inputdata &input ) {}; } class CEnemy : public CEntity { public: virtual void draw(); // implemented... virtual void update() { do_ai_stuff(); } // use the default null impl of handleinput because enemies don't care... } class CPlayer : public CEntity { public: virtual void draw(); virtual void update(); virtual void handleinput( const inputdata &input) {}; // handle input here }
然后实体管理器遍历并在世界上的每个实体上调用update(),handleinput()和draw().
当然,这些函数有很多功能,当你调用它们时,其中大部分都不做任何事情,可能会浪费很多,特别是对于虚拟函数.所以我也看过一些其他的方法.
一个是将输入数据存储在全局(或作为全局接口或单例等的成员)中.然后覆盖敌人的update()函数,所以他们do_ai_stuff().和更新(),以便它通过轮询全局进行输入处理.
另一个是在Listener pattern上使用一些变体,所以关心输入的所有内容都继承自一个普通的监听器类,并且通过InputManager注册所有这些监听器.然后,输入管理器依次调用每个侦听器:
class CInputManager { AddListener( IInputListener *pListener ); RemoveListener( IInputListener *pListener ); vector<IInputListener *>m_listeners; void PerFrame( inputdata *input ) { for ( i = 0 ; i < m_listeners.count() ; ++i ) { m_listeners[i]->handleinput(input); } } }; CInputManager g_InputManager; // or a singleton,etc class IInputListener { virtual void handleinput( inputdata *input ) = 0; IInputListener() { g_InputManager.AddListener(this); } ~IInputListener() { g_InputManager.RemoveListener(this); } } class CPlayer : public IInputListener { virtual void handleinput( inputdata *input ); // implement this.. }
还有其他更复杂的方法.但所有这些工作,我已经看到他们中的每一个实际运输和销售的东西.