在SFML中使用ImGui

太之初一 posted @ 2017年3月13日 17:09 in 编程 with tags c++ OpenGL 编程 GUI编程 ImGui , 219 阅读

SFML是一个类似于SDL的图形库,不同的是,SFML是面向对象的,并且完全使用OPENGL,而SDL在某些情况下会使用DirectX。这就导致了一个问题:当我们想使用ImGui这种基于OPENGL的GUI库的时候,需要将SDL创建的窗口交给OPENGL管理,即一旦在SDL中使用ImGui,就不能再使用它的2D渲染功能,两者会互相覆盖,导致画面闪来闪去。之前我使用SDL开发了一些简单的2D游戏的雏形,但是一到GUI方面就卡住了。

ImGui是我见过的使用最方便的GUI之一,没有回调函数之类麻烦的操作,可以在短时间内开发出功能强大的控件。同时,它对Unicode支持良好,中文也不是问题。所以我全面转战SFML,利用GitHub上别人开发的一个SFML-ImGui的绑定,在SFML中使用ImGui。

 

这是一个典型的SFML程序:

#include <SFML/Window.hpp>

int main()
{
    sf::Window window(sf::VideoMode(800, 600), "My window");

    // run the program as long as the window is open
    while (window.isOpen())
    {
        // check all the window's events that were triggered since the last iteration of the loop
        sf::Event event;
        while (window.pollEvent(event))
        {
            // "close requested" event: we close the window
            if (event.type == sf::Event::Closed)
                window.close();
        }
    }

    return 0;
}

要使用ImGui,需要按如下步骤操作(翻译自imgui-SFML项目README文件):

  • 下载 ImGui

  • 将ImGui的目录添加到你的项目的包含目录

  • 将 imgui.cpp 和 imgui_draw.cpp 添加到你的项目

  • 将 imconfig-SFML.h 中的内容复制到 imconfig.h 里面。 (这是为了实现 ImVec2 到 sf::Vector2f 的转换等操作)

  • 将 imgui-SFML.h 所在目录添加到你的项目的包含目录

  • 将 imgui-SFML.cpp 添加到你的项目

在你的代码中:

  • 调用 ImGui::SFML::Init 并将 sf::Window + sf::RenderTarget 或 sf::RenderWindow 作为参数传入。你也同时创建并传入你自己的字符集到Init方法中,否则程序会自动创建一个默认的字符集。(重点,后面会讲)

  • 在游戏主循环的每一次循环中:

    • 抓取并处理事件:sf::Event event;

      while (window.pollEvent(event)) {
          ImGui::SFML::ProcessEvent(event);
          ...
      }
    • 调用 ImGui::SFML::Update(window, deltaTime) ,其中 deltaTime 是 sf::Time 类型的变量。

    • 调用ImGui函数进行绘制和处理 (ImGui::Begin()ImGui::Button(), 等等)

    • 如果你在渲染之前进行了多次更新,调用 ImGui::EndFrame 。(需要包含 imgui_internal.h )

    • 调用 ImGui::Render()

  • 在程序结束时调用 ImGui::SFML::Shutdown() 。

 

示例代码:

#include "imgui.h"
#include "imgui-SFML.h"

#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/System/Clock.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Graphics/CircleShape.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(640, 480), "ImGui + SFML = <3");
    window.setFramerateLimit(60);
    ImGui::SFML::Init(window);

    sf::CircleShape shape(100.f);
    shape.setFillColor(sf::Color::Green);

    sf::Clock deltaClock;
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            ImGui::SFML::ProcessEvent(event);

            if (event.type == sf::Event::Closed) {
                window.close();
            }
        }

        ImGui::SFML::Update(window, deltaClock.restart());

        ImGui::Begin("Hello, world!");
        ImGui::Button("Look at this pretty button");
        ImGui::End();

        window.clear();
        window.draw(shape);
        ImGui::Render();
        window.display();
    }

    ImGui::SFML::Shutdown();
}

这样就可以使用ImGui了,注意示例中SFML和ImGui各项操作的顺序,任意颠倒可能出现问题。ImGui的使用见GitHub主页。

 

两个重点:

1. imgui-SFML目前有个良性bug:如果你把某些东西从main函数拿出去,可能完全正常的代码会出现不能渲染的情况。这时你需要在ImGui::Render()之前调用一次window.resetGLStates(),就能解决问题。作者本来是希望让绑定自己调用这个的,除了这个bug,还没找到原因,但手动调用就行了,不影响使用

2. 使用中文:imgui-SFML内部自己创建的字符集不支持Unicode,当然就显示不了中文,所以我们必须自己创建字符集。但是这里有个问题,如果用作者说的向Init()函数传入字符集,那么我们需要一个sf::Texture类型的变量,可是如果我们用SFML的字体模块读入字体文件,获取到的sf::Texture是const的,用不成。所以我用如下代码解决了问题:

ImGui::SFML::Init(window);
ImGuiIO& io = ImGui::GetIO();
io.Fonts->ClearFonts();
io.Fonts->AddFontFromFileTTF("xarialuni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesChinese());
sf::Texture *font_texture = new sf::Texture;
ImGui::SFML::createFontTexture(*font_texture);
ImGui::SFML::setFontTexture(*font_texture);

经过上述操作后,还需要在普通字符串之前加个u8前缀,就能正确渲染其中的中文字符了。如:u8"我是中文"。


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter