- 1. Projektstruktur
- 2. mainwindow.ui
- 3. widget.h
- 4. widget.cpp
- 5. dreieck.h
- 6. Dreieck.cpp
- 7. Notiz
- 8. Ergebnis
- 9. Videoanleitung
Dieses Tutorial beginnt mit einer Reihe von Artikeln zum Schreiben eines Spiels in Qt. Im vorherigen Artikel wurde über das Positionierungssystem von Grafikelementen QGraphicsItem in der Grafikszene QGraphicsScene. Ein Dreieck wurde gezeichnet und in der Mitte der Grafikszene platziert, die Abmessungen betrugen 500 x 500 Pixel. Und nun ist es an der Zeit, dieses Dreieck wiederzubeleben bzw. zu managen.
Lassen Sie uns die technische Aufgabe der Lektion erstellen:
- Das Fenster enthält eine Grafikszene mit den Maßen 500 x 500 Pixel (dies wurde bereits in der vorherigen Lektion durchgeführt);
- In der Mitte der Grafikszene befindet sich ein rotes Dreieck (was auch in der letzten Lektion gemacht wurde);
- Das Dreieck sollte sich beim Drücken der Pfeiltasten nach oben, unten, links, rechts bewegen;
- Das Dreieck darf nicht über die Grafikszene hinausgehen, dh es muss durch die Abmessungen der Grafikszene begrenzt werden.
Hinweis Dieses Projekt verwendet WinAPI, daher ist das Projekt für die Verwendung im Windows-Betriebssystem anwendbar, und für Linux und MacOS ist nur der in dieser Lektion verwendete Algorithmus anwendbar. Wenn Sie also ein Spiel für diese Betriebssysteme schreiben möchten, müssen Sie die Bibliotheken dieser Betriebssysteme für die asynchrone Verarbeitung von Tastenanschlägen verwenden.
Projektstruktur
- Triangle.pro - Projektprofil, standardmäßig erstellt und erfordert keine Anpassungen in diesem Projekt;
- main.cpp - die Datei, aus der die Anwendung startet, in dieser Datei wird das Widget aufgerufen, in dem sich die grafische Szene mit dem Dreieck befindet, die wir steuern werden;
- widget.h - Header-Datei des aufgerufenen Widgets mit einer grafischen Szene;
- widget.cpp - Quellcodedatei des Widgets;
- Triangle.h - Header-Datei der Klasse Triangle , die von QGraphicsItem erbt;
- Triangle.cpp - Quellcodedatei für Klasse Triangle.
mainwindow.ui
Werfen Sie im Interface-Designer einfach QGraphicsView in das Widget. Nichts anderes ist erforderlich.
widget.h
In dieser Datei deklarieren wir nur die Grafikszene, das Dreieckobjekt, das wir steuern werden, sowie den Timer, der verwendet wird, um den Zustand der Tastaturtasten zu überprüfen, mit denen wir das Dreieck in der Grafikszene steuern.
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QGraphicsScene> #include <QShortcut> #include <QTimer> #include <triangle.h> namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private: Ui::Widget *ui; QGraphicsScene *scene; /// Объявляем графическую сцену Triangle *triangle; /// и треугольник QTimer *timer; /* Объявляем игровой таймер, благодаря которому * будет производиться изменения положения объекта на сцене * При воздействии на него клавишами клавиатуры * */ }; #endif // WIDGET_H
widget.cpp
In dieser Datei werden die Grafikszene und ihre Abmessungen initialisiert, und die Grenzen des Felds, in dem sich das Dreieck bewegt, werden auch in der Grafikszene gezeichnet. Der Schlüsselpunkt ist jedoch die Initialisierung des Timers nach der Verarbeitung des Signals, von dem aus der Zustand der Grafikszene geändert wird, und die Koordinaten der Position des Dreiecks ändern sich auch, während der Zustand der Tastaturtasten überwacht wird.
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); this->resize(600,600); /// Задаем размеры виджета, то есть окна this->setFixedSize(600,600); /// Фиксируем размеры виджета scene = new QGraphicsScene(); /// Инициализируем графическую сцену triangle = new Triangle(); /// Инициализируем треугольник ui->graphicsView->setScene(scene); /// Устанавливаем графическую сцену в graphicsView ui->graphicsView->setRenderHint(QPainter::Antialiasing); /// Устанавливаем сглаживание ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); /// Отключаем скроллбар по вертикали ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); /// Отключаем скроллбар по горизонтали scene->setSceneRect(-250,-250,500,500); /// Устанавливаем область графической сцены scene->addLine(-250,0,250,0,QPen(Qt::black)); /// Добавляем горизонтальную линию через центр scene->addLine(0,-250,0,250,QPen(Qt::black)); /// Добавляем вертикальную линию через центр /* Дополнительно нарисуем органичение территории в графической сцене */ scene->addLine(-250,-250, 250,-250, QPen(Qt::black)); scene->addLine(-250, 250, 250, 250, QPen(Qt::black)); scene->addLine(-250,-250,-250, 250, QPen(Qt::black)); scene->addLine( 250,-250, 250, 250, QPen(Qt::black)); scene->addItem(triangle); /// Добавляем на сцену треугольник triangle->setPos(0,0); /// Устанавливаем треугольник в центр сцены /* Инициализируем таймер и вызываем слот обработки сигнала таймера * у Треугольника 20 раз в секунду. * Управляя скоростью отсчётов, соответственно управляем скоростью * изменения состояния графической сцены * */ timer = new QTimer(); connect(timer, &QTimer::timeout, triangle, &Triangle::slotGameTimer); timer->start(1000 / 50); } Widget::~Widget() { delete ui; }
dreieck.h
Und jetzt fahren wir mit dem Programmcode fort, der für das grafische Objekt verantwortlich ist, das wir verwalten werden. Die Klasse erbt von QObject für die Arbeit mit Signalen und Slots und auch von QGraphicsItem.
In dieser Datei ist die Header-Datei windows.h verbunden, um mit der Funktionalität zu arbeiten
#ifndef TRIANGLE_H #define TRIANGLE_H #include <QObject> #include <QGraphicsItem> #include <QPainter> #include <QGraphicsScene> /* Подключаем библиотеку, отвечающую за использование WinAPI * Данная библиотека необходима для асинхронной проверки состояния клавиш * */ #include <windows.h> class Triangle : public QObject, public QGraphicsItem { Q_OBJECT public: explicit Triangle(QObject *parent = 0); ~Triangle(); signals: public slots: void slotGameTimer(); // Слот, который отвечает за обработку перемещения треугольника protected: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); private: qreal angle; // Угол поворота графического объекта }; #endif // TRIANGLE_H
Dreieck.cpp
Das Zeichnen eines Dreiecks stammt aus der vorherigen Lektion zum Positionieren von Grafikelementen in einer Grafikszene, aber das Neuzeichnen in dem Slot, der das Signal vom Timer verarbeitet, sowie das Initialisieren der anfänglichen Drehung des Objekts ist bereits ein neuer Code.
Die Drehung des Objekts wird in Grad der Variable angle angegeben und durch die setRotation () Funktion festgelegt, die von QGraphicsItem geerbt wurde. Um den Zustand der Tastaturschaltflächen zu verfolgen, wird außerdem eine Funktion von WinAPI verwendet, nämlich GetAsyncKeyState (), , die den Zustand dieser Schaltfläche durch den Schaltflächencode bestimmt. Bei jedem Signal von einem Objekt der Klasse QTimer werden die gedrückten Tasten überprüft und die Position des Dreiecks ändert sich je nach Zustand.
#include "triangle.h" Triangle::Triangle(QObject *parent) : QObject(parent), QGraphicsItem() { angle = 0; // Задаём угол поворота графического объекта setRotation(angle); // Устанавилваем угол поворота графического объекта } Triangle::~Triangle() { } QRectF Triangle::boundingRect() const { return QRectF(-25,-40,50,80); /// Ограничиваем область, в которой лежит треугольник } void Triangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { QPolygon polygon; /// Используем класс полигона, чтобы отрисовать треугольник /// Помещаем координаты точек в полигональную модель polygon << QPoint(0,-40) << QPoint(25,40) << QPoint(-25,40); painter->setBrush(Qt::red); /// Устанавливаем кисть, которой будем отрисовывать объект painter->drawPolygon(polygon); /// Рисуем треугольник по полигональной модели Q_UNUSED(option); Q_UNUSED(widget); } void Triangle::slotGameTimer() { /* Поочерёдно выполняем проверку на нажатие клавиш * с помощью функции асинхронного получения состояния клавиш, * которая предоставляется WinAPI * */ if(GetAsyncKeyState(VK_LEFT)){ angle -= 10; // Задаём поворот на 10 градусов влево setRotation(angle); // Поворачиваем объект } if(GetAsyncKeyState(VK_RIGHT)){ angle += 10; // Задаём поворот на 10 градусов вправо setRotation(angle); // Поворачиваем объект } if(GetAsyncKeyState(VK_UP)){ setPos(mapToParent(0, -5)); /* Продвигаем объект на 5 пискселей вперёд * перетранслировав их в координатную систему * графической сцены * */ } if(GetAsyncKeyState(VK_DOWN)){ setPos(mapToParent(0, 5)); /* Продвигаем объект на 5 пискселей назад * перетранслировав их в координатную систему * графической сцены * */ } /* Проверка выхода за границы поля * Если объект выходит за заданные границы, то возвращаем его назад * */ if(this->x() - 10 < -250){ this->setX(-240); // слева } if(this->x() + 10 > 250){ this->setX(240); // справа } if(this->y() - 10 < -250){ this->setY(-240); // сверху } if(this->y() + 10 > 250){ this->setY(240); // снизу } }
Notiz
Damit das Projekt mit dem MSVC-Build-Kit kompiliert werden kann, fügen Sie der pro-Projektdatei die folgenden Zeilen hinzu:
win32-msvc*{ LIBS += -luser32 }
Ergebnis
Als Ergebnis der geleisteten Arbeit haben wir die ersten Schritte zum Schreiben eines Spiels unternommen. Wir haben nämlich gelernt, wie man ein Objekt steuert, also unser Heldendreieck, mit dem wir auch in zukünftigen Lektionen an unserem ersten Spiel arbeiten werden.
Das Video-Tutorial bietet zusätzliche Kommentare und Erläuterungen zum Projekt und demonstriert die Bedienung der Anwendung.
Vollständige Liste der Artikel dieser Serie:
- Lektion 1. Wie man ein Spiel in Qt schreibt. Objektverwaltung
- Lektion 2. Wie man ein Spiel in Qt schreibt. Animation des Spielhelden (2D)
- Lektion 3. Wie man ein Spiel in Qt schreibt. Interaktion mit anderen Objekten
- Lektion 4. Wie man ein Spiel in Qt schreibt. Der Feind ist der Sinn des Überlebens
- Lektion 5. Wie man ein Spiel in Qt schreibt. Ton von QMediaPlayer hinzufügen
Добрый день,
Подскажите пожалуйста, чем может быть вызвана ошибка
"C:\Android\Qt\Mark\untitled4\triangle.cpp:5: ошибка: undefined reference to `vtable for Triangle'" ?
В конструкторе указывает на наследуемый класс QGraphicsItem и деструктор Triangle.
Я сначала пытался код сам исправить, но ничего не вышло и я просто скопировал Ваш.
Скопировал triangle.h и triangle.cpp.
Что это может быть?
Отвратительная ошибка на самом деле.
Вы как всегда правы, удалить билд и перезапустить было решением проблемы
Добрый день.
Проблема следующая:
треугольник при перемещении не отрисовывается полностью, кроме того, на графической сцене остаются его следы, но если, например, свернуть приложение и заново его открыть, то треугольник отрисовывется как положено.
Добрый день. Переопределили boundingRect? и переопределили ли его правильно? выглядит так, что либо boundingRect неправильно расчитывается, либо не вызывается update сцены.
Спасибо, не поставил минус в boundingRect
Евгений здравствуйте!Только начинаю разбираться в QT и работаю в Unreal Engine 4(делаю игры и приложения).Можно ли их сравнивать или они для разных задач.И что не сделать в Unreal,что можно сделать в QT.Оба на с++,у обоих есть дизайнеры для интерфейса.В Unreal правда blueprints,а в QT Qml(я так понимаю это его плюс перед Unreal).На Qml можно писать красивый интерфейс,но в Unreal это делать проще?
Добрый день
Они для разных задач.
В Qt вам для игры придётся написать собственный движок, который будет отвечать за физику и т.д. А в Unreal Engine 4 придётся писать очень много подготовительной логики для моделей данных (таблицы и т.д.). Ну либо использовать Felgo - фреймворк для написания приложений, который написан поверх Qt, они там сделали большую работу для мобильных приложений. В качестве движка используют cocos2d для игр.
Делать вёрстку приложения на QML определённо проще, якоря очень хорошая вещь. Когда я пробовал делать простую вёрстку в UE4 для меню, то мне якорей очень сильно не хватало, которые есть в QML.
blueprints - это конёчно жёсткое псевдопрограммирование )))) работать с ними можно, но я прекрасно понимал, когда пробовал себя в UE4, что логику, которую я писал в blueprints 8-10 блоками , на С++ я мог бы написать в две строки кода, которые были бы предельно понятны. Поэтому сразу начал искать варианты переноса логики в C++.
blueprints хороши для того, что требует 3D представления, но биты и байты ими перебирать - это самовредительство.
В общем сравнивать можно по ощущениям, но точно не по назначению. UE4 - игры, а Qt - это всё остальное.
Евгений, спасибо за развёрнутый ответ!
"В дизайнере интерфейсов просто закидываем QGraphicsView в виджет. Больше ничего не требуется."
Для тупеньких, объясните пж (Qt Creator 4.10.2)
В Qt Creator двойным кликом открыть ui файл, и перетащить в виджет Graphics View. Потом выделить корневой виджет и нажать кнопку для создания GridLayoyt, чтобы Graphics View нормально позиционировался.
Спасибо, и пока пользуясь случаем. PushButton в основном для чего?
Это обычная кнопка, клик по этой кнопке можно привязать к какому угодно функционалу, от применения настроек до открытия диалога. Зависит от логики, которую в неё вкладывают.
Если у вас и дальше будут вопросы, то задавайте их на форуме, каждый в отдельной теме, чтобы они уже были логически структурированы и не было оффтопов.
Я просматриваю все вопросы, которые появляются на форуме и по возможности отвечаю на них. Форум
Может вы мне подскажете, ибо я не могу понять и найти как эти строчки изменить под linux:
Под Linux Не подскажу, там всё несколько сложнее
Привет, такой вопрос. Пишу игру, где отрисовываю абсолютно всё через QPainter. Спрайты анимации вывожу через DrawImage(). Какой из вариантов (QPainter и QGraphicsView) лучше в плане производительности? По поводу функционала понятно, но интересует именно скорость отрисовки, так как на средних смартфонах бывает падает FPS