После того, как мы нарисовали 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, с ростом опыта разберётесь, как это сделать, но я могу вам только общее направление по этой задаче подсказать и кое-какие примеры, но вопросы у вас должны быть более гранулированными, а не так, "как написать программу, которая сделает то, что вы хотите". Серьёзно, для того, что вам нужно, требуется изучить больше, чем вот эта одна строка из вашего кода
значительно больше
А как вы сделали папку ресурсы ?