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

Making my game independent from graphic API in C++

本文关键字:图形 API 于图形 独立 C++ 我的 游戏      更新时间:2023-10-16

我想让我的游戏独立于所使用的底层图形 API(以便在我想将其移植到另一个平台时减少耦合(。

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

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

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

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

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

请参阅Irrlicht,它有一个简单但良好的架构,可以满足您在可移植性方面的标准。基本模式是适配器、调解器和工厂方法。这种便携性的关键是良好的界面。我们需要GraphicsApiMeshRendererTexture的抽象类,以及所有图形API的实现。

对于 DirectX9:

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

对于 OpenGL:

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

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

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

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

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

SDL 和 OpenGL ES 在

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

我的解决方案(从我的爱好黑客 2d 游戏引擎中剪切的片段(看起来像这样。我从任何类型的纹理的句柄开始(这是独立于图形库的(:

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是一个纯虚拟接口。

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