在实体组件引擎中获取组件到它们各自的系统

我在stackoverflow问这个问题,但我没有得到任何答复。 我认为这个交换更适合。

我正在构建一个各种各样的游戏引擎(纯教育游戏),我不确定当使用系统共享的对象时,保持大型系统解耦的最佳方式。

我的引擎正在使用一个实体组件devise,其中组件只包含状态。 系统是使这些组件运行的驱动力,所以我需要能够在各自的系统上注册组件。 我只是不确定如何通过层次结构看起来像这样的组件:

当前Heirarchy

我可以想到一些处理这个问题的方法。

选项1:单身。 如果我使我的引擎对象单身我可以使用静态方法直接在我的input组件绑定到input系统。

class InputComponent: public Component { InputComponent() { Engine::getEngine()->getInputSystem()->bindInputComponent( this ); } virtual ~InputComponent() { Engine::getEngine()->getInputSystem()->unbindInputComponent( this ); } } 

这有利于组件自动绑定和解除绑定到他们的系统,这是方便的。 它有一个单身人士(不一定是坏的,但它可能会使我的引擎在未来僵化)和InputComponent“知道”InputSystem这可能是坏的devise的失败。

scheme2:从现场系统下来。 我可以让我的场景系统负责绑定和解除绑定组件。

 class SceneSystem: public System { void setActiveScene( Scene* scene ) { // Deactivate (and unset components) for the current scene. unsetActiveScene(); for( auto i : scene->getEntities() ) { for( auto j : i->getAllComponents<InputComponent>() ) { // Send message through mediator to bind the component DataMessage<InputComponent*> bindMessage( MESSAGE_BIND_INPUT_COMPONENT, j ); Message response; sendMessage( &bindMessage, &response, SYSTEM_INPUT ); } } } } 

通过这种方式,input组件并不担心查找层次结构。 但现在我的场景系统必须“知道”input组件(以及所有其他需要注册的组件)。 这是耦合我的系统。

另外,如果我需要添加/删除组件,我需要通过场景系统来完成。

选项3:从input组件进行处理。 我可以想象的最后一件事情就是改变input组件的层次结构。 我想在这种情况下,我也会添加一个通用的bindComponent到现场系统,所以没有太多复杂的中介信息蔓延到我的组件。

 class SceneSystem: public System { template <typename T> void bindComponent( T* component, int system ) { DataMessage<T*> bindMessage( MESSAGE_BIND_COMPONENT, component ); Message response; sendMessage( &bindMessage, &response, system); } } class InputComponent: public Component { public: InputComponent( Entity* parentEntity ) : m_parentEntity( parentEntity ) { m_parentEntity->getParentScene()->getSceneSystem()->bindComponent<InputComponent>( this, SYSTEM_INPUT ); } virtual ~InputComponent() { m_parentEntity->getParentScene()->getSceneSystem()->unbindComponent<InputComponent>( this, SYSTEM_INPUT ); } private: Entity* m_parentEntity; } 

这有我的层次结构是双向的(不知道这是否是一件坏事)

这些select中,更好地保持这样一个系统的灵活性和合理解耦。 同时指出其他select,特别是如果我目前的devise和/或对问题的理解存在缺陷。

这有利于组件自动绑定和解除绑定到他们的系统,这是方便的。 它有一个单身人士(不一定是坏的,但它可能会使我的引擎在未来僵化)和InputComponent“知道”InputSystem这可能是坏的devise的失败。

总的来说,实际的基本引擎是一个单例,不存在真正的问题,因为经常需要对象和内存分配一次,无论内部需要同时运行多less事情。

您所说的灵活性可以通过引擎实例本身和其内部组件之间的良好devise轻松解决。 对此的一种方法是使用一些象表示引擎全局状态的对象的上下文。

 InputComponent(Context* context) { context->GetSystem<InputSystem>()->BindComponent( this ); } 

通过这种方式,input组件并不担心查找层次结构。 但现在我的场景系统必须“知道”input组件(以及所有其他需要注册的组件)。 这是耦合我的系统。

有必然会有一些耦合,但我同意我不喜欢这种方法。 我想我应该考虑一些callback机制,如果你想从现场的角度来看。

  1. 创建一个附加到给定场景的实体。
  2. 创建组件数据类并将其附加到给定的实体实例。
  3. 激活实体,触发一系列callback和系统注册。

这里的概念的基础是,一个实体基本上从系统的角度来说是不存在的,直到它被激活。 一旦一个实体被激活,你通常不能改变它的组件列表,除非你停用它,修改并重新激活它。

 void Entity::Activate() { for( System* system : GetScene()->GetSystems() ) system->OnEntityActivated( this ); } 

这个想法是,系统查询实体并根据存在的组件来确定实体是否应该在该系统中注册。 如果不是,系统什么都不做。 如果是这样,则系统将该实体添加到用于更新循环迭代的一些内部数据结构。

总的来说你的devise对我来说似乎很好

如果是我,我倾向于使用我后面的建议,因为它避免了紧密的耦合,并允许在现场和系统之间以最小的契约进行灵活的devise。

我在YouTube上看了一篇关于SFML上的游戏引擎devise的教程,这已经成为我的devise基石: https : //www.youtube.com/watch?v= tgXEws1Ef8s&list=PLRtjMdoYXLf4md1jeCkdPJYpDgPkjT9Di

其中,他们使用状态机来expression他们的层次结构。 有一个结构对象在状态之间传递,因为每个状态都依次加载。 国家被视为自己的游戏个别…启animation面可能有一个状态,主菜单是一个,游戏是一个,暂停菜单是一个,等等。每个状态要么暂停或另一个被添加时销毁。 这种状态之间的工作方式是,每个国家负责调用另一个,反过来也可以传递数据。