- 1. Project structure
- 2. sprite.h
- 3. sprite.cpp
- 4. bullet.h
- 5. bullet.cpp
- 6. Result
- 7. Video
In previous articles we have learned to draw a sprite image and apply it to Qt via QPixmap so we turned animated explosion. And now we need to put this very explosion at precisely the place where the bullet hits. That is, the bullet will explode.
For the implementation of this ideas will add class sprite from the past lessons, and modify it. The fact is that the sprite object is a class inherited from QGraphicsItem , and therefore a bullet when confronted with the explosion of another bullet will behave as well as in a collision with an obstacle or a target. Therefore it is necessary to do so to ignore the bullets from the other explosions and bullets flying through.
Project structure
The project is subject to modification in the sense that a new class is added to the following files:
- sprite.h
- sprite.cpp
This class will be called animated sprite explosion, after which this frame object will be removed from the graphic scene.
sprite.h
Compared with the code from lesson on connecting animated sprites in Qt in this class must override the virtual function type(), and replace the type identifier of our graphic object to distinguish it from the other types of identifiers of graphic objects. For example Type will be equal UserType + 1 instead UserType .
#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(QPointF point, QObject *parent = 0); /* Override the type of graphic object explosion that bullet could ignore this object * */ enum { Type = UserType + 1 }; // Also override function for the object type int type() const; signals: public slots: private slots: void nextFrame(); /// Slot for turning frames private: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); QRectF boundingRect() const; private: QTimer *timer; /// The timer for the animation of the explosion QPixmap *spriteImage; /// QPixmap for the sprite with the explosion int currentFrame; /// Coordinate of the current frame in sprite }; #endif // SPRITE_H
sprite.cpp
The source code will also attend the change. In addition, we are redefining the type() function. We also makes changes to the slot to switch sprite frames. Initially looping animation has been set in the code. In this case, at the expiration of all of the frames need to be deleted from a graphics scene object that is implemented in this slot.
Also there is a small change in the Sprite class constructor. In it added an argument that specifies the position of an explosion on the graphic scene. That is, the bullet will pass in this class is the place of the explosion.
#include "sprite.h" Sprite::Sprite(QPointF point, QObject *parent) : QObject(parent), QGraphicsItem() { this->setPos(point); // Set the position of the blast currentFrame = 0; /// X coordinate of the beginning of the explosion of bullets spriteImage = new QPixmap(":/sprites/sprite_sheet.png"); timer = new QTimer(); /// Initialize timer explosion animation connect(timer, &QTimer::timeout, this, &Sprite::nextFrame); timer->start(25); /// We start the timer at 25 milliseconds } QRectF Sprite::boundingRect() const { return QRectF(-10,-10,20,20); } void Sprite::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { // Draw one of the frames of the explosion painter->drawPixmap(-10,-10, *spriteImage, currentFrame, 0, 20,20); Q_UNUSED(option); Q_UNUSED(widget); } void Sprite::nextFrame() { currentFrame += 20; // We change the X coordinate to select the next frame if (currentFrame >= 300 ) { this->deleteLater(); // If shots are over, then remove the explosion object } else { this->update(-10,-10,20,20); } } int Sprite::type() const { return Type; }
bullet.h
In this file, only connect the Sprite class.
bullet.cpp
But in the Bullet class we change a few lines of code. It will be necessary to find in slotTimerBullet() function section of code that is responsible for checking on the fact of collision with other objects.
This part is a foreach loop. In it we add a check for a collision with another explosion, and if such a collision does not exist, but there is a clash with any other object, you create an explosion and destroy the bullet.
The nuance of this code is that we do not object to qgraphicsitem_cast sprite class. Since the creation of this object is assigned (UserType + 1) , and QGraphicsItem has a function type(), then it is enough to determine what object hit a bullet.
void Bullet::slotTimerBullet() { setPos(mapToParent(0, -10)); QList<QGraphicsItem *> foundItems = scene()->items(QPolygonF() << mapToScene(0, 0) << mapToScene(-1, -1) << mapToScene(1, -1)); foreach (QGraphicsItem *item, foundItems) { /* Add to the test more and explosions themselves * to ignore their bullets and exploding hitting in explosion from another bullet * */ if (item == this || item == hero || item->type() == (UserType + 1)) continue; // When you hit the target or wall, causes an explosion scene()->addItem(new Sprite(this->pos())); callbackFunc(item); // Вызываем CallBack функцию this->deleteLater(); // Уничтожаем пулю } if(this->x() < 0){ this->deleteLater(); } if(this->x() > 500){ this->deleteLater(); } if(this->y() < 0){ this->deleteLater(); } if(this->y() > 500){ this->deleteLater(); } }
Result
As a result of the work done in the field impact of the bullet will be formed bursts, as shown in the figure.
Link to project downloading: targetmotion.zip
Скажите, пожалуйста, а такой код void Widget::slotBullet(QPointF start, QPointF end) { /// Добавляем на сцену пулю Bullet *bullet = new Bullet(start, end, triangle); bullet->setCallbackFunc(slotHitTarget); scene->addItem(bullet); } Ведет к утечке памяти или нет? Ведь оператор new есть, а delete нет.
Извините, я не знаю по какой причине комментарий продублировался несколько раз. Еще и форматирование слетело.
Кнопку комментировать несколько раз нажимали? или один? Всё ещё не получается отловить этот плавающий баг с повторной отправкой. Ну да ладно.
Вообще, при добавлении Item`а на графическую сцену, графическая сцена становится владельцем этого item`а, что сказано в документации на этот метод
Следовательно, учитывая специфику Qt фреймворка, что parent удаляет своих children при разрушении, можно не беспокоиться об утечке памяти в данном моменте, поскольку графическая сцена при удалении должна будет удалить свои Item`ы.
Нажимал несколько раз, но страница перезагрузилась один раз. Спасибо за ответ.
Комментарии добавляются через AJAX, перезагрузка страницы не требуется. Впрочем, нужно будет учесть момент, когда соединение проходит не сразу. Спасибо.
Добрый вечер! Скажите пожалуйста почему при отладке данного проекта компилятор находит ошибку в файле widget.cpp: "'targ' was not declared in this scope" ?
Добрый вечер!
Я скачал весь готовый проект и когда запустил вышла эта ошибка
Проверил. Там не должно быть такой ошибки, поскольку существует только одно место в проекте, где используется имя targ и в том коде ошибки быть не должно. Если бы переменная была удалена, тогда да, а так она присутствует.
Добрый вечер! Я извиняюсь за очередное беспокойство, но у меня все проги с "QGraphicsItem *item" пишут что item не декларирован, может у кого-то была такая ошибка и как ее исправить?
Добрый... А вы какую IDE используете, какую версию Qt, под какой платформой собираете? Linux, windows? Проект под Linux так-то собираться не будет, там есть плафтормозависимый код.
Windows 7 (64 бит), Qt Creator 4.5.0 (Community)
Ну... А какая конкретно версия Qt у вас используется. Я вот сейчас на работе собрал проект под Qt 5.9.3 компилятором MSVC 2015.
Код писался пару лет назад, возможно в новых библиотеках некоторые изменения произошли. Там в некоторых местах для есть макросы foreach, обычно их уже переписывают на новый стандарт, поскольку нужды в этом макросе уже нет.
Опять ругается: "range-based 'for' loops are not allowed in C++98 mode". QT 5.5, компилятор mingw492_32
Всё, проблема решена путём переустановки Qt на версию 5.9.3. Никаких проблем с "QGraphicsItem *item"))
Здравствуйте!
Вбил
Удалил файлы сборки, пересобрал, запустил qmake, запустил проект - запустилось.Добрый день! Извиняюсь за поздний ответ. Выходные выдались довольно напряжёнными.
C++98 mode - это режим стандарта 98, устаревший стандарт, нужно использовать минимум c++11 сейчас.
Ну да, переустановка видимо решила проблему с настройкой стандарта. Хорошо )) Извиняюсь за поздний ответ, забитые выходные были, некогда было даже глянуть активность на сайте.
Немножко не понял к чему относится упоминание про C++98 mode.
У человека ошибка была
спасибо за пояснение.
Если вдруг кто-то прочитает....
Скачал проект, скомпилил, запустил. Всё красиво и объектно ориентировано, но вот FPS дико страдает, когда появляется 10+ врагов. Может есть какие-то надстройки? Ведь если нет, то в чём вообще практический смысл QGraphicView/Scene/Items, если всё то же самое и даже с более тяжелыми анимацией и физикой с отрисовкой на том же QPainter летает даже на самом дровяном ноутбуке?