C++ 用于绘制游戏组件的重构代码

C++ 用于绘制游戏组件的重构代码,c++,architecture,refactoring,C++,Architecture,Refactoring,如何使用折射器将图形函数从h文件移动到GraphicsManager类中 //drawingFunctions.h void drawTexturedQuad( Texture texture, Vector2 pos, Vector2 dim) { // bind texture... glBegin(...); // draw //... glEnd(...); } //class file #include "drawingFunctions.h

如何使用折射器将图形函数从h文件移动到GraphicsManager类中

//drawingFunctions.h
void drawTexturedQuad( Texture texture, Vector2 pos, Vector2 dim) {
    // bind texture...
    glBegin(...);    // draw  
    //...
    glEnd(...);
}

//class file
#include "drawingFunctions.h"
class Player { 
    void drawPlayer(){ drawTexturedQuad( texture, pos, dim) } 
};
class Enemy { 
    void drawEnemy(){ drawTexturedQuad( texture, pos, dim) } 
};
class Item { 
    void drawItem(){ drawTexturedQuad( texture, pos, dim) } 
};
// and so on for the other components

//gameloop file
// instantiate components objects
while (true) {
    // input, logic
    Player.drawPlayer();
    Enemy.drawEnemy();
    Item.drawItem();
    // and so on
}
(代码明显简化了,我只是问一下这里的图纸)

我应该

  • 将指向GraphicsManager的指针传递给游戏循环中drawPlayer、Drawnemy等的每次调用
  • 让玩家、敌人等有一个指向GraphicsManger的指针作为数据成员
  • 让玩家、敌人等扩展一个drawableGameComponent类,该类将指向GraphicsManager的指针作为数据成员
  • 还有别的吗

这听起来像是继承的完美用例:

class Drawable
{
public:
    void draw()
    {
         // gl stuff
    }

protected:
     Texture _texture;
     Vector2 _pos;
     Vector2 _dim;
};


class Player : Drawable
{
public:
     // should modify _texture _pos and _dim somewhere.       
};

// same thing for the other objects.

我会选择第一种可能性:在调用中传递指向GraphicsManager的指针。尽管这看起来有点过分,但关于使用哪个GraphicsManager的知识在更高的级别上进行了维护,以后可以更轻松地进行修改

话虽如此,我仍然会从一个可绘制的接口继承,并将需要绘制的项放在一个容器中,这样您就可以通过一个virtual drawItem()函数对其进行迭代以显示这些项。 与此类似(C++03,未测试):

std::向量项;
物品。推回(&player);
物品。推回(和敌人);
...
对于(std::vector::iterator it=Items.begin();it!=Items.end():++it)
{
(*it)->图纸项目(&graphMgr);
}

我会将渲染器传递给模型,并要求它自己绘制

class Player
{
public:
    void draw(Renderer& renderer);
};

class Enemy
{
public:
    void draw(Renderer& renderer);
};
注意:您不必为函数命名
drawPlayer
drawnemy
,因为您已经知道它是
播放器
敌人
。此统一调用约定非常适合提取到公共接口:

class Model
{
public:
    virtual void draw(Renderer& renderer) = 0;

    virtual ~Model() {}
};
然后,您可以让您的每个模型继承自
Model
,并让每个实现
draw

正如我在对@J.N.答案的评论中提到的,您还可以将Renderer设置为抽象类。例如,我在一个使用OpenGL、GDI+的项目上工作,还需要创建原理图的打印输出

class Renderer
{
public:
    virtual render(const Triangle& triangle, const Texture& texture) = 0;

    virtual ~Renderer() {}
};

如果你想使用cough directx,或者SDL,或者测试渲染器来确定回归呢?@PeterWood:我想这不是问题所在。我在
Drawable
中没有提到任何构造函数,如果您需要进行一些初始化,没有什么可以阻止您添加它。@PeterWood好吧,在
draw()
中,最好调用适当的呈现器方法,而不是直接插入OpenGL代码。你能更详细地解释你答案的第二部分吗?我所理解的是
virtual
部分,其中我给出了(抽象)
Drawable
class a
virtual
成员函数
drawaitem()
。我不明白的是关于容器的部分,或者至少这两个部分是如何关联的?谢谢编辑。我认为这是另一个问题。目前,我实际上是在使用管理器类(EnemyManager、ItemManager)来维护组件列表,然后在渲染时遍历list元素的draw方法。@Ben:这就是我写“说到这里”的原因。在您的示例中,它解决了不同非虚拟函数的顺序调用问题。对于示例代码,它现在主导了真正的答案。为了确保我正确理解它,您的
draw(renderer)
将调用
render(…)
,对吗?除此之外:为什么我更喜欢在每次调用
draw(…)
时将引用传递给renderer,而不是将其作为模型中的数据成员并将其传递给从模型派生的组件的构造函数?我想,您可以在运行时更改它。我的意思是,
Renderer
将具有许多用于渲染各种对象(如文本、三角形、圆、位图等)的功能,因此每个模型使用的渲染器不同。但是再看一次你的例子,我可能错误地认为它被简化了,你调用
drawTexturedQuad
的方式与调用每个例子的方式完全相同。因此,绘图代码中没有我允许的变化,也许@J.N的答案就是您所需要的。以后可能会有变化。那么,为什么要引用渲染器而不是将指针传递给组件的构造函数呢?我还是不明白原因是什么?@Ben也许模型是在渲染器之前创建的,也许渲染器会改变。可能需要在不同的窗口中渲染相同的模型。它还使模型更小。我更喜欢引用,因为它们从来都不是无效的,所以不需要检查。关于这个话题,已经有过几次讨论,也许值得一看。
class Renderer
{
public:
    virtual render(const Triangle& triangle, const Texture& texture) = 0;

    virtual ~Renderer() {}
};