使我的游戏独立于C ++中的图形API

时间:2013-12-13 21:23:25

标签: c++ design-patterns

我想让我的游戏与所使用的底层图形API无关(为了减少耦合,以防我想将它移植到另一个平台)。

我目前正在使用SDL,最初我想将SDL_Renderer封装在GraphicAPI_SDL类中,实现我的通用GraphicAPI接口。此API将创建Image_SDL(封装SDL_Texture),它实现了我的通用Image接口。

我的问题是,当我想使用我的GraphicAPI实例渲染图像时,底层的GraphicAPI_SDL必须将Image转换为Image_SDL,以获得SDL_Texture。这样的演员很难看。

处理这类情况的最佳方法是什么?我将把每个加载在一个向量中的SDL_Texture存储在我的GraphicAPI_SDL中,每个Image的实例只有一个对应于向量中纹理索引的整数,因此只有GraphicAPI_SDL类直接使用SDL,但是有一个更好的方法(例如使用模式)?

4 个答案:

答案 0 :(得分:1)

如果您正在使用SDL 2.0(并且只是制作2D游戏),那么您已经在很大程度上与底层的Graphics API分离。当您设置窗口时,SDL 2.0可以为您的系统自动选择正确的渲染器(DirectX或OpenGL),您的代码根本不需要担心它。但是,如果您的游戏需要较低级别的绘图控制(即,您正在制作3D游戏),则需要自己处理渲染器。

有关详细信息,请参阅此处:http://wiki.libsdl.org/MigrationGuide#Video

答案 1 :(得分:0)

请参阅Irrlicht,它有一个简单但很好的架构,符合您的可移植性标准。基本模式为adaptermediatorfactory method。这种便携性的关键是良好的界面。我们需要GraphicsApiMeshRendererTexture的抽象类,以及所有图形API的实现。

对于DirectX9:

  • GraphicsApiDX9实施GraphicsApi
  • MeshRendererDX9实施MeshRenderer
  • TextureDX9实施Texture

对于OpenGL:

  • GraphicsApiGL实施GraphicsApi
  • MeshRendererGL实施MeshRenderer
  • TextureGL实施Texture

等等。 GraphicsApiDX9创建TextureDX9,保留指向IDirect3DTexture9的内部指针,以及创建纹理的IDirect3DDevice9。您正在使用创建的一个槽Texture接口,因此您不关心它是如何创建的。

答案 2 :(得分:0)

首先,没有平台独立性这样的东西。在每个平台上,您都被迫做出一些假设,这些假设在您的下一个平台上可能会或可能不会成立。在任何抽象平台中,您将做出一些假设(如平台支持纹理,矩阵变换,四元数,无3D支持等)。抽象平台所做的一切都会产生不必要的复杂性。

当您考虑支持2个或更多平台时,抽象平台才开始有意义,此时您可以抽象出常见概念(如纹理和矩阵变换)。

因此,除非您计划支持多个平台,否则您可以做的最好的事情就是确保您的应用程序正确分层。

SDL和OpenGL ES在大量平台上得到支持,除非您计划将游戏移植到专业游戏机,否则SDL和OpenGL ES涵盖了大多数基础。

答案 3 :(得分:0)

我的解决方案(从我的爱好 - 黑客 - 第二代 - 游戏引擎中删除的片段)就像这样。我从任何一种纹理的句柄开始(这是独立于图形库):

struct MGTexHandle
{
    MGTexHandle();
    ~MGTexHandle();
    void* tex;
};

然后,在我的图形库接口类中,我保留了所有方法的通用接口,没有库特定类型。实现必须移植到每个图形库(我测试的唯一一个是SDL2)。

例如,将图像加载到与图形库无关的纹理对象(texHandle参数):

void* MGWindow::loadBMPImage(std::string fileName, bool transparent) 
{
    SDL_Surface* loadedImage = NULL;
    SDL_Texture* optimizedImage = NULL;
    loadedImage = SDL_LoadBMP(fileName.c_str());
    if(loadedImage != NULL)
    {
        if(transparent)
        {
            // TODO: Make it possible to have other color codes than
            // zero represent transparency
            SDL_SetColorKey(loadedImage, SDL_TRUE, 0);
        }
        optimizedImage = SDL_CreateTextureFromSurface(m_Renderer, loadedImage);
        SDL_FreeSurface(loadedImage);
    }
    return (void*)optimizedImage;
}

void MGWindow::loadBMPImage(std::string fileName, MGTexHandle &texHandle, bool transparent)
{
    if(texHandle.tex)
    {
        SDL_DestroyTexture(static_cast<SDL_Texture*>(texHandle.tex));
    }
    texHandle.tex = loadBMPImage(fileName, transparent);
}

在屏幕上渲染纹理如下所示:

void MGWindow::drawSprite(const MGTexHandle &imageTexture, int srcX, int srcY, int dstX, int dstY, int width, int height)
{
    increaseDrawnSpritesCounter();
    SDL_Rect srcRect;
    srcRect.x = srcX;
    srcRect.y = srcY;
    srcRect.w = width;
    srcRect.h = height;
    SDL_Rect dstRect;
    dstRect.x = dstX;
    dstRect.y = dstY;
    dstRect.w = width;
    dstRect.h = height;
    SDL_RenderCopy(m_Renderer, static_cast<SDL_Texture*>(imageTexture.tex), &srcRect, &dstRect);
}

标题的开头如下:

class MGWindow : public IMGWindow
{
    private:
        int m_Width;
        int m_Height;
        int m_Bpp;
        bool m_Fullscreen;
        std::string m_Title;
        SDL_Window* m_Screen;
        SDL_Renderer* m_Renderer;
...

IMGWindow是纯虚拟界面。

我可以模拟和存根图形并执行非常复杂的自动化集成测试,涉及除库特定图形例程之外的所有组件。这就是界面的重点,而不是有一天能够移植到另一个库,但现在也应该非常直接。