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