SFML ist ein objektorientiertes Analogon von SDL. Die Bibliothek bietet einfachen Zugriff auf E/A-, Audio-, Datennetzwerk- und OpenGL-Funktionen.
Aber was, wenn wir dieser Bibliothek zusätzlich Qt-Funktionalität hinzufügen, die mächtigen Fähigkeiten von Signalen und Slots, Qt-Ressourcendateien unter Kontrolle bekommen? Was wird dafür benötigt?
- Laden Sie die neuesten SFML -Quellen von GitHub herunter.
- Quellen in eine Bibliothek kompilieren
- Erstellen Sie ein Qt-Projekt
- Verbinden Sie die Bibliothek mit dem Projekt
- Schreiben Sie ein Widget, das direkt über die SFML-Funktionalität gerendert wird
SFML erstellen
Im Allgemeinen können Sie vorkompilierte SFML-Bibliotheken herunterladen, aber wenn Sie einen anderen Compiler als den haben, mit dem diese Bibliotheken erstellt wurden, wird nichts entsprechend funktionieren. Daher experimentieren wir nicht, sondern kompilieren die Bibliothek gleich mit dem Compiler, mit dem wir das Projekt bauen werden.
SFML wird durch ein Projekt repräsentiert, das das Build-System CMAKE verwendet, das sich problemlos in Qt Creator öffnet.
Wenn das Projekt geöffnet ist, starten wir CMAKE und erstellen dann die Bibliothek selbst, sowohl im Debug-Modus als auch im Release-Modus, um zwei Sätze von Bibliotheken zu haben.
Das Build-Verzeichnis enthält das lib-Verzeichnis, das die für uns interessanten Bibliotheken enthält. Wir werden sie dem Qt-Projekt hinzufügen, das SFML verwenden wird.
Die folgenden Bibliotheken werden erstellt:
- smfl-Audio
- sfml-Grafiken
- sfml-Netzwerk
- sfml-Fenster
- sfml-System
Qt-Projekt
Als nächstes erstellen Sie ein neues Qt-Projekt. Erstellen wir ein Projekt mit QWidget als Hauptanwendungsfenster. Nachdem das Projekt erstellt wurde, fügen Sie das SFML-Verzeichnis zum Projektstammverzeichnis hinzu, das zwei Verzeichnisse enthält: lib und include.
Alle kompilierten SFML-Bibliotheken müssen im lib-Verzeichnis abgelegt werden, sowohl für die Debug-Version als auch für die Release-Version. Und im include -Verzeichnis müssen Sie Dateien aus demselben include-Verzeichnis platzieren, das sich im Quellprojekt der SFML-Bibliothek befindet.
Als Ergebnis sieht die Struktur dieses Verzeichnisses in Ihrem Qt-Projekt wie folgt aus.
Nachdem die Bibliothek vorbereitet ist, muss sie über ein Profil in das Qt-Projekt eingebunden werden.
Schreiben Sie dazu folgende Zeilen in die Profildatei:
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/SFML/lib/ -lsfml-audio -lsfml-graphics -lsfml-network -lsfml-window -lsfml-system else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/SFML/lib/ -lsfml-audio-d -lsfml-graphics-d -lsfml-network-d -lsfml-window-d -lsfml-system-d else:unix: LIBS += -L$$PWD/SFML/lib/ -lsfml-audio -lsfml-graphics -lsfml-network -lsfml-window -lsfml-system INCLUDEPATH += $$PWD/SFML/include DEPENDPATH += $$PWD/SFML/include
$$PWD - bezeichnet den Speicherort der Pro-Projektdatei, in die wir die Projektkonfiguration und die Include-Dateien schreiben. Das heißt, wir suchen nach allen erforderlichen Bibliotheksdateien relativ zur Pro-Projektdatei.
Und jetzt fangen wir an, das Projekt selbst zu schreiben. Lassen Sie uns ein SFML-Widget erstellen, das in das Hauptanwendungs-Widget passt, um zu zeigen, dass Sie nicht das gesamte Anwendungsfenster aufgeben müssen, um SFML zu verwenden.
Die Projektstruktur wird wie folgt aussehen:
Wie Sie sehen, gibt es ein Hauptfenster-Widget Widget und ein qsqmlcanvas-Widget, in dem die SFML-Bibliothek funktioniert. Ich habe auch ein Bild über eine Qt-Ressourcendatei hinzugefügt ... Ja! Das ist möglich! Es ist möglich, Qt-Ressourcendateien in einer SFML-Bibliothek zu verwenden, es sind nur ein paar zusätzliche Codezeilen erforderlich, um die Bilder ordnungsgemäß aus der Ressourcendatei zu extrahieren.
Beginnen wir nun mit der Erstellung eines Projekts mit Qt und SFML. Es wurde beschlossen, eine Anwendung zu erstellen, in deren SFML-Widget sich das Qt-Logo drehen wird.
Projektprofil und main.cpp
Es wurde oben erwähnt, dass die Bibliothek in das Projekt aufgenommen wird, und die Datei main.cpp unterliegt keinen Änderungen, daher wird sie sich nicht darauf konzentrieren.
Widget.h
In diesem Projekt werde ich keinen Grafikdesigner verwenden, also schreiben wir manuell ein QGridLayout in die Header-Datei des Fensters, das das SFML-Widget hosten wird.
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QGridLayout> #include "qsfmlcanvas.h" // Подключение виджета SFML class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget* parent = nullptr); ~Widget(); private: QSFMLCanvas* m_sfmlWidget; QGridLayout* m_gridLayout; }; #endif // WIDGET_H
Widget.cpp
Und im Konstruktor erstellen wir ein QSFMLCanvas-Widget und legen seine Platzierung im Anwendungsfenster fest.
#include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { resize(640, 480); m_sfmlWidget = new QSFMLCanvas(this); m_gridLayout = new QGridLayout(this); m_gridLayout->addWidget(m_sfmlWidget, 0, 0); } Widget::~Widget() { }
qsfmlcanvas.h
Analysieren wir den Inhalt der SFML-Header-Klasse des Widgets.
#ifndef QSFMLCANVAS_H #define QSFMLCANVAS_H #include <QWidget> #include <QTimer> #include <SFML/Graphics.hpp> class QSFMLCanvas : public QWidget, public sf::RenderWindow { Q_OBJECT public: explicit QSFMLCanvas(QWidget* parent = nullptr, uint frameTime = 50); virtual ~QSFMLCanvas() {} private : // Метод первоначальной инициализации виджета void onInit(); // Метод обновления содержимого виджета void onUpdate(); // Метод, возвращающий движок отрисовки Qt virtual QPaintEngine* paintEngine() const override; // Метод событий открытия и закрытия виджета, понадобится для первоначальной инициализации virtual void showEvent(QShowEvent*) override; // Метод отрисовки, понадобится нам для перерисовки виджета virtual void paintEvent(QPaintEvent*) override; private slots: // Слот, в котором будет вызываться событие перерисовки void onTimeout(); private: QTimer m_timer; bool m_initialized; sf::Texture m_texture; sf::Sprite m_sprite; }; #endif // QSFMLCANVAS_H
Die Methoden onInit() und onUpdate() sind optional und Sie können sie tatsächlich benennen, wie Sie möchten. Wichtig ist, dass Sie zunächst alle erforderlichen Objekte hinzufügen und regelmäßig aktualisieren müssen.
Aber die Methode paintEngine() muss überschrieben werden, da wir die Qt-Rendering-Engine nicht verwenden werden, dann muss diese Methode nullptr. zurückgeben.
Die Methoden showEvent() und paintEvent() werden verwendet, um die SFML-Funktionalität und das anfängliche Rendern und Aktualisieren des Renderns von Objekten zu initialisieren. Dies sind Methoden, die Methoden der QWidget -Klasse sind, von der die SFML-Widget-Klasse abgeleitet ist. Das Neuzeichnen des SFML-Widgets mit diesen Methoden erfolgt innerhalb des Qt-Ereignis-Frameworks. Was bedeutet das? Tatsache ist, dass die paintEvent() -Methode aufgerufen werden kann, indem die repaint() -Methode aufgerufen wird, repaint() löst ein repaint-Ereignis innerhalb des Aufrufstapels der Qt-Anwendung aus, also ist es nicht überraschend das zum Neuzeichnen wird nicht durch einen direkten Aufruf der Methode paintEvent(). ausgeführt
Sie haben vielleicht auch bemerkt, dass diese Klasse Mehrfachvererbung von zwei Klassen verwendet: QWidget und sf::RenderWindow.
Wir benötigen die Vererbung von QWidget , um ein SFML-Widget überall in die Anwendungsschnittstelle einbetten zu können und auf die Funktionalität von Qt-Signalen und -Slots zuzugreifen, während die Vererbung von sf::RenderWindow für den Zugriff erforderlich ist Funktionalität dieser Bibliothek, außerdem arbeitet sie direkt mit OpenGL zusammen.
qsqmlcanvas.cpp
#include "qsfmlcanvas.h" #include <string> #include <QPixmap> #include <QByteArray> #include <QBuffer> QSFMLCanvas::QSFMLCanvas(QWidget *parent, uint frameTime) : QWidget(parent), // Инициализируем базовый конструктор окна отрисовки SFML sf::RenderWindow(sf::VideoMode(800, 600), "OpenGL", sf::Style::Default, sf::ContextSettings(24)), m_initialized(false) { // Произведём настройку для непосредственной отрисовки изображения в виджет setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_NoSystemBackground); // Установим строгую фокусировку для включения событий клавиатуры setFocusPolicy(Qt::StrongFocus); // Настроим период сработки таймера перерисовки m_timer.setInterval(frameTime); } void QSFMLCanvas::onInit() { // Загрузка изображения из ресурсных файлов Qt // Довольно интересный момент в том плане, что // SFML не может загрузить изображение из ресурсов, как это делают QPixmap или QImage QPixmap pixmap(":/QtCreator.png"); // Но если создать объект QPixmap QByteArray bArray; // Создать объект массива байтов QBuffer buffer(&bArray); // Поместить его в буфер buffer.open(QIODevice::WriteOnly); // Сохранить изображение в этот буфер, то есть в память pixmap.save(&buffer, "PNG"); // А потом забрать сырые данные из буффера, с указанием количества забираемых данных m_texture.loadFromMemory(buffer.data().data(), buffer.data().size()); // Эту процедуру понадобится произвести один раз при создании текстуры SFML // Далее устанавливаем изображения в sprite m_sprite.setTexture(m_texture); float xScale = 200. / m_sprite.getTextureRect().width; float yScale = 200. / m_sprite.getTextureRect().height; m_sprite.setScale(xScale, yScale); // Масштабируем изображение m_sprite.setOrigin(m_sprite.getTextureRect().width / 2, m_sprite.getTextureRect().height / 2); // Устанавливаем центр изображения m_sprite.setPosition(200, 200); // Устанавливаем позицию изображения } void QSFMLCanvas::onUpdate() { // Очищаем экран clear(sf::Color(128, 0, 0)); // Поворачиваем изображение m_sprite.rotate(1); // Отрисовываем это изображение draw(m_sprite); } QPaintEngine* QSFMLCanvas::paintEngine() const { // Возвращаем nullptr вместо движка отрисовки Qt, чтобы Qt не пытался что либо рисовать сам return nullptr; } void QSFMLCanvas::showEvent(QShowEvent*) { // Первичная инициализация виджета SFML if (!m_initialized) { // Создаём SFML окно для отрисовки с указанием Id окна, в котором будет производиться отрисовка RenderWindow::create(winId()); // Инициализация объектов отрисовки onInit(); // Настройка таймера для перезапуска отрисовки виджета connect(&m_timer, &QTimer::timeout, this, &QSFMLCanvas::onTimeout); m_timer.start(); m_initialized = true; } } void QSFMLCanvas::paintEvent(QPaintEvent*) { // Обновление отрисовки объектов onUpdate(); // Отображение отрисованного окна display(); } void QSFMLCanvas::onTimeout() { // Запуск перерисовки repaint(); }
ACHTUNG: Das Projekt wurde unter KDE NEON 5.8.2 erstellt und ist ein Beispiel, dessen korrekter Start unter anderen Betriebssystemen nicht gewährleistet ist, insbesondere müssen Sie die SFML-Bibliothek für Ihre Zielplattform und erstellen mit deinem Compiler.
Здравствуйте!
Собирал под Windows 7. Компилировал с тем же компилятором, с которым работает Qt
Но если эту строку закомментировать, то приложение запускается и создается 2 окна(никакого изображения конечно же нет). OpenGl пустое, а второе с черными квадратными областями
Два окна из-за того, что хендлер не был передан для отрисовки.
Нет, так есть тож не хочет
Более полный текст ошибки тогда давайте. Наверняка там есть более подробная информация. Пока у меня идей нет.
Когда следую вашему методу с cast пишет "invalid conversion from long unsigned int to sf::WindowHandle {aka HWND__*}' [fpermissive]"
Ну тогда такой каст
Теперь пишет тоже самое просто вместо unsigned long int - "WId{aka unsigned int} "
а вот так все работает RenderWindow::create(sf::WindowHandle(winId()));
Забавно. Какой компилятор использовали?
открывается только 1 окно но картинка не грузится
mingw
А нет все отлично работает и с jpg просто это из-за размеров картинки была проблема
Спасибо за советы