После того, как мы нарисовали sprite в прошлом уроке по работе с Adobe Illustrator, настало время применить полученную картинку при работе с Qt и добавить её в программу с помощью QPixmap. Причем, мы сделаем анимированый sprite, и посмотрим, как происходит маленький анимированый взрыв на графической сцене нашего приложения на Qt.
Структура проекта для работы с QPixmap и sprite
Структура проекта sprite_example будет следующая:
- sprite_example.pro - профайл проект;
- widget.h - заголовочный файл основного окна приложения;
- widget.cpp - файл исходных кодов основного окна приложения;
- widget.ui - файл интерфейса;
- sprite.h - заголовочный файл класса, предназначенного для нашего спрайта, в котором будет применяться QPixmap;
- sprite.cpp - файл исходных кодов для работы с QPixmap;
- sprite.qrc - ресурсный файл;
- sprite_sheet.png - наш спрайт, который мы применим для анимации.
widget.ui
Ничего особенного в этом файле нет. Просто растягиваем по всему окно объект QGraphicsView, в который поместим QGraphicsScene .
widget.h
В этом файле мы объявим объект графической сцены, в который поместим объект с QPixmap, в котором будет анимированый sprite. Только не забудьте подключить заголовочный файл sprite.h.
- #ifndef WIDGET_H
- #define WIDGET_H
- #include <QWidget>
- #include <QGraphicsScene>
- #include <QTimer>
- #include <QList>
- #include <QPixmap>
- #include "sprite.h"
- namespace Ui {
- class Widget;
- }
- class Widget : public QWidget
- {
- Q_OBJECT
- public:
- explicit Widget(QWidget *parent = 0);
- ~Widget();
- private:
- Ui::Widget *ui;
- QGraphicsScene *scene; // Объявляем графическую сцену
- };
- #endif // WIDGET_H
widget.cpp
В данном файле инициализируем графическую сцену и помещаем её в graphicsView, а также создаём объект спрайта и помещаем его на сцену.
- #include "widget.h"
- #include "ui_widget.h"
- Widget::Widget(QWidget *parent) :
- QWidget(parent),
- ui(new Ui::Widget)
- {
- ui->setupUi(this);
- scene = new QGraphicsScene(); // Инициализируем графическую сцену
- ui->graphicsView->setScene(scene); // Устанавливаем графическую сцену в graphicsView
- scene->addItem(new Sprite()); // Помещаем на сцену новый объект спрайта
- }
- Widget::~Widget()
- {
- delete ui;
- }
sprite.h
А вот теперь поговорим о том, как же реализован анимированый sprite. Для этого нам понадобится заранее подготовленный спрайт, а вернее sprite sheet, который состоит из 15 кадров, расположенных в одну строку.
Технически, данный sprite sheet устанавливается в объект класса QPixmap, в котором он пролистывается слева направо с определенной периодичность. Для этого мы новый класс наследуем от QGraphicsItem и QObject, чтобы использовать сигналы и слоты . И в методе paint задаём отрисовку участка изображения sprite_sheet , который задан в ресурсном файле. Таймер управляет сменой кадров с помощью слота nextFrame(). По сигналу от таймера мы изменяем координату отрисовки участка спрайта и перерисовываем графический объект с новым кадром.
- #ifndef SPRITE_H
- #define SPRITE_H
- #include <QObject>
- #include <QGraphicsItem>
- #include <QTimer>
- #include <QPixmap>
- #include <QPainter>
- class Sprite : public QObject, public QGraphicsItem
- {
- Q_OBJECT
- public:
- explicit Sprite(QObject *parent = 0);
- signals:
- public slots:
- private slots:
- void nextFrame(); // Слот для пролистывания изображения в QPixmap
- private:
- void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
- QRectF boundingRect() const;
- private:
- QTimer *timer; // Таймер для пролистывания изображения в QPixmap
- QPixmap *spriteImage; // В данный объект QPixamp будет помещён спрайт
- int currentFrame; // Координата X, с которой начинает очередной кадр спрайта
- };
- #endif // SPRITE_H
sprite.cpp
В конструкторе класса проводим инициализацию таймера, а в методе paint отрисовываем нужный нам кадр из спрайта. Переключение кадров производится с помощью слота nextFrame().
- #include "sprite.h"
- Sprite::Sprite(QObject *parent) :
- QObject(parent), QGraphicsItem()
- {
- currentFrame = 0; // Устанавливаем координату текущего кадра спрайта
- spriteImage = new QPixmap(":sprite_sheet.png"); // Загружаем изображение спрайта в QPixmap
- timer = new QTimer(); // Создаём таймер для анимации спрайта
- // Подключаем сигнал от таймера к слоту перелистывания кадров спрайта
- connect(timer, &QTimer::timeout, this, &Sprite::nextFrame);
- timer->start(25); // Запускаем спрайт на генерацию сигнала с периодичность 25 мс
- }
- QRectF Sprite::boundingRect() const
- {
- return QRectF(-10,-10,20,20);
- }
- void Sprite::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
- {
- /* В отрисовщике графического объекта отрисовываем спрайт
- * Разберём все параметры данной функции
- * Первых два аргумента - это координат X и Y куда помещается QPixmap
- * Третий аргумент - это указатель на QPixmap
- * 4 и 5 аргументы - Координаты в В изображении QPixmap, откуда будет отображаться изображение
- * Задавая координату X с помощью перемнной currentFrame мы будем как бы передвигать камеру
- * по спрайту
- * и последние два аргумента - это ширина и высота отображаем части изображение, то есть кадра
- * */
- painter->drawPixmap(-10,-10, *spriteImage, currentFrame, 0, 20,20);
- Q_UNUSED(option);
- Q_UNUSED(widget);
- }
- void Sprite::nextFrame()
- {
- /* По сигналу от таймера передвигаем на 20 пикселей точку отрисовки
- * Если currentFrame = 300 то обнуляем его, поскольку размер sprite sheet 300 пикселей на 20
- * */
- currentFrame += 20;
- if (currentFrame >= 300 ) currentFrame = 0;
- this->update(-10,-10,20,20); // и перерисовываем графический объект с новым кадром спрайта
- }
Итог
В результате у Вас получится приложение, в середине окна которого будет находится моргающий взрыв размером 20 на 20 пикселей. Демонстрацию работы приложения Вы можете увидеть в видеоуроке.
Подскажите пожалуйста ( я новичок совсем)
Можно ли организовать спрайт без этого окошка (как в fl studio fruity dance)?
Не знаю, какой-там конкретно эффект и если честно не хочется fl studio ради того, чтобы посмотреть устанавливать, но из того, что увидел в интернете.
Предполагаю, что то, что вы хотите сделать, в данном случае нужно реализовывать точно также с окном, но с одним существенным отличием. Нужно полностью отключить обрамление окна, а в нём отрисовывать спрайт в виджете с прозрачным фоном.
Вот в этой статье было что-то похожее. Там я отключал обрамление окна и кастомизировал виджеты.
Обратите внимание вот на эти строчки
Спасибо большое!
Очень помогли!
А возможно всё кроме спрайта сделать прозрачным?
"всё" - это что?
по факту я вам уже ответил и на этот вопрос.
Извините, но дилемма в том что - Спрайт должен быть сам по себе без заднего окна и виджета. ( вот то что я хочу сделать-
Прошу прощения за надоедливые и банальные вопросы
Да не в этом дело.
По сути, там всё тоже самое, только отображайте спрайт в отдельном диалоговом окне, у которого будет полностью отключено обрамление и т.д. Просто используйте QDialog, чтобы спрайт отдельно крутился.
С Новым годом!
Всё оказалось проще:
MAIN.cpp
QML
У меня всего 2 вопроса:
\\
Как В файле QML менять значение с помощью переменной из Main frameX: 0 не через класс? ( желательно считывание из txt файла)
\\\
Как сделать так чтобы курсор мышки не реагировал на прозрачное диалоговое окно и я мог спокойно нажимать на ярлыки позади него
Ну вы здесь тоже самое сделали, что я вам и советовал... только ещё QML за уши притянули.
frameX - только через класс устанавливать. В QML все объекты наследованы от QObject и там работа с классами идёт. Максимум использовать JavaScript встроенный в QML, но опять же без C++ и классов ничего вы не сделаете.
Это было бы проще реализовать через классические виджеты, также нужно лезть в API операционной системы. Я такое не делал, небоходимости не было. Могу сказать только, что нужно искать информацию по эмулированию кликов, чтобы перекинуть клик за окно приложения. В общем в три строки эо не делается.
Планомерно изучайте Qt, с ростом опыта разберётесь, как это сделать, но я могу вам только общее направление по этой задаче подсказать и кое-какие примеры, но вопросы у вас должны быть более гранулированными, а не так, "как написать программу, которая сделает то, что вы хотите". Серьёзно, для того, что вам нужно, требуется изучить больше, чем вот эта одна строка из вашего кода
значительно больше
А как вы сделали папку ресурсы ?