【问题标题】:How to best manage components in a game engine?如何最好地管理游戏引擎中的组件?
【发布时间】:2017-06-25 14:23:32
【问题描述】:

所以我正在开发我的第一个游戏引擎,但我碰壁了。目前我有一个实体基类,它有一个组件指针向量,它将保存指向某些系统管理器类(graphisManager、PhysicsManager 等)中的组件对象的指针。这是我当前的实体标题(精简以关注主要问题):

Entity.h

class Component;

namespace BlazeGameWorld
{
    class Entity
    {
    public:
        BlazeFramework::Math::Vector2D position;

    protected:
        Vector<Component*> components;

        static BlazeGraphics::GraphicsManager graphicsManager;
        static BlazePhysics::PhysicsManager physicsManager;
        static BlazeInput::InputManager inputManager;
        //....other managers

    private:

        ///////////////////////////////////////////////////////////////////////

    public:
        Entity();
        ~Entity();

        virtual bool Initialize();
        virtual bool Shutdown();

        virtual void Update() = 0;

        void AddComponent(Component* p_component);

        //Part of the broadcast messaging system for components to be able
        //to talk to one another in a decoupled way.
        void SendMessage(uint messageID);

    protected:

    private:
    };
}

如您所见,我们的想法是使用静态 SystemManager 类来管理指向堆上实际组件的指针。这是潜在的 PhysicsManager 类的粗略 Header(其他 Manager 类也类似):

PhysicsManager.h

class PhysicsComponent;

namespace BlazePhysics
{
    class PhysicsManager
    {
    public:

    protected:
        int numPhysicsComponents;
    private:
        Vector<PhysicsComponent*> physicsComponents;

        /////////////////////////////////////////////////////////////

    public:
        PhysicsManager();
        ~PhysicsManager();

        bool Initialize();
        bool Shutdown();

        void Update();

        template <typename PhysicsComponentType>
        PhysicsComponentType* CreatePhysicsComponent();

    private:
    };

    //template definitions
    template <typename PhysicsComponentType>
    PhysicsComponentType* PhysicsManager::CreatePhysicsComponent()
    {
        PhysicsComponentType* physicsComponent = new PhysicsComponentType
        physicsComponents.push_back(physicsComponent);

        return physicsComponents.at(numPhysicsComponents++);
    }
}

所以我可以将所有不同的physicsComponent 指针存储在PhysicsManger 向量中(指向CollisionComponents、PositionComponents 等的指针)。问题是如果我想调用特定于特定物理组件的方法,我无法编译。例如,如果(在 PhysicsManager 的更新循环中)我想在每一帧更新一个碰撞组件的 CheckCollision() 方法,我不能只在 for 循环中说 physicsComponents.at(i).CheckCollision因为编译器在编译时不知道什么是 CollisionComponent是。有没有办法先推断出数组中组件的类型,然后如果它匹配 CollisionComponent 调用 CheckCollision 方法?还是有更好的方法来实现这一点,因为这看起来有点笨拙?

【问题讨论】:

  • 很高兴在这里看到基于组件的引擎,我也在开发我的第一个游戏引擎,其结构与您的相似。但是,我将游戏引擎的引用传递给每个系统,以便他们可以使用该引用访问其他系统。我不知道它是否好,但至少它正在工作。希望在这里看到更好的解决方案 XD
  • 很高兴听到您的工作进展顺利!我正在研究一些实现不同组件类型数组的解决方案。如果我能弄清楚如何实现类似的东西,我可能会自己在这里发布一个解决方案,哈哈。

标签: c++ architecture components game-engine


【解决方案1】:

实体不应该了解您的系统,它们应该只是组件的集合。否则引入另一个引擎系统的过程也需要更改实体类,这违背了 ECS 的全部目的。

系统也不应该管理组件。一个系统可以使用多个组件,并且许多系统可以使用,例如位置/碰撞几何组件。

所以,在我看来:

  • 理想情况下,组件应该是简单的纯数据类,而所有处理都在系统内完成。
  • 实体只能提供一种添加和删除组件的方式,并且 告诉它是否有特定的组件。
  • 某种实体管理器必须以缓存友好的方式存储组件,以便系统访问它们,并且应该能够提供实体列表,这些实体具有系统的特定组件/标签集。

这样,例如,如果您想为某些实体添加脚本行为,您所要做的就是添加ScriptComponentScriptingSystem。您现有的所有代码都不需要任何更改。

This question 在这个主题上有很多非常有用的资源。

【讨论】:

  • 好的,所以你说实体管理器类应该负责处理游戏中实体的所有组件?并且所有系统(graphicsSystem、PhysicsSystem、ScriptingSystem)都可以使用和交换组件吗?
  • @Jason 可以(并且可能更好)称为更具描述性的东西,但绝对不是系统本身。通常,系统不会像您的世界/场景那样添加/删除组件。有很多关于 ECS 和一些开源实现的论文,你一定要看看。
  • @Jason 另外,Vittorio Romeo 在 CppCon15 上有一个 nice talk on the implementation of ECS
猜你喜欢
  • 2011-04-10
  • 2016-09-21
  • 1970-01-01
  • 1970-01-01
  • 2017-08-01
  • 2015-07-05
  • 1970-01-01
  • 2010-12-26
  • 2012-01-14
相关资源
最近更新 更多