GameDev on Qt - Tutorial 5. The explosion of bullets using sprite images

GameDev, Bullet, QPixmap, Qt, sprite, sprite sheet, explosion. взрыв, Спрайт

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.


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
    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;


public slots:

private slots:
    void nextFrame();   /// Slot for turning frames

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    QRectF boundingRect() const;

    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


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);

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 {

int Sprite::type() const {
    return Type;


In this file, only connect the Sprite class.


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))
        // 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){
    if(this->x() > 500){

    if(this->y() < 0){
    if(this->y() > 500){


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:


We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
Support the author Donate

Скажите, пожалуйста, а такой код void Widget::slotBullet(QPointF start, QPointF end) { /// Добавляем на сцену пулю Bullet *bullet = new Bullet(start, end, triangle); bullet->setCallbackFunc(slotHitTarget); scene->addItem(bullet); } Ведет к утечке памяти или нет? Ведь оператор new есть, а delete нет.


Извините, я не знаю по какой причине комментарий продублировался несколько раз. Еще и форматирование слетело.

void Widget::slotBullet(QPointF start, QPointF end)
    /// Добавляем на сцену пулю
    Bullet *bullet = new Bullet(start, end, triangle);

Кнопку комментировать несколько раз нажимали? или один? Всё ещё не получается отловить этот плавающий баг с повторной отправкой. Ну да ладно.

Вообще, при добавлении Item`а на графическую сцену, графическая сцена становится владельцем этого item`а, что сказано в документации на этот метод

This scene takes ownership of the 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, обычно их уже переписывают на новый стандарт, поскольку нужды в этом макросе уже нет.

foreach (QGraphicsItem *targ, targets) 

for (QGraphicsItem *targ : targets) 

Попробуйте так переписать проблемные места.

Опять ругается: "range-based 'for' loops are not allowed in C++98 mode". QT 5.5, компилятор mingw492_32


Всё, проблема решена путём переустановки Qt на версию 5.9.3. Никаких проблем с "QGraphicsItem *item"))



Скачал проект чтобы попробовать. Не собирается:
triangle.obj:-1: ошибка: LNK2019: unresolved external symbol __imp_GetAsyncKeyState referenced in function "private: void __cdecl Triangle::slotGameTimer(void)" (?slotGameTimer@Triangle@@AEAAXXZ)
Windows 10, Qt 5.8.0(MSVC 2015, 32 бита).

Подскажите, пожалуйста, что нужно сделать?


    LIBS += -luser32
Удалил файлы сборки, пересобрал, запустил qmake, запустил проект - запустилось.
Где-то писали, что работает на windows и есть проблемы с отлавливанием событий нажатий клавиш. Что код получается системно зависимым.

Правильно понимаю, что на Linux работать не будет?

Добрый день! Извиняюсь за поздний ответ. Выходные выдались довольно напряжёнными.

Да, вы правы, к сожалению данный код платформозависимый и конкретно данные примеры применимы только в рамках WinAPI. У меня так и не дошли руки до изучения отлавливания нажатий клавиш в рамках Linux. Увы...

C++98 mode - это режим стандарта 98, устаревший стандарт, нужно использовать минимум c++11 сейчас.

Ну да, переустановка видимо решила проблему с настройкой стандарта. Хорошо )) Извиняюсь за поздний ответ, забитые выходные были, некогда было даже глянуть активность на сайте.


Немножко не понял к чему относится упоминание про C++98 mode.

По поводу клавиш. Думал раньше, что Qt сам реализует платформонезависимые элементы, в т.ч. отслеживание нажатие клавиш. А тут как-то странно получилось.

У человека ошибка была

"range-based 'for' loops are not allowed in C++98 mode"
Это означает, что у него компилятор собирает проект со стандартом C++98, а range-based циклы были введены только в стандарте C++11. Поэтому проект и не собирался.

По поводу платформозависимых частей. Qt реализует очень много функционала кросплатформенно, но есть функционал, который он не реализует, приходится писать платформозависимый код и разруливать его через Pimpl.
Например, глобальные хоткеи или логирование нажатий клавиш, когда приложение находится в фоновом режиме. Это Qt не реализует, нужно лезть в библиотеки системы. Тоже самое касается и отслеживание входа пользователя через RDP под Windows. Нужно использовать WinAPI для этого, под Linux это придётся делать через DBus. Под Андроидом придётся писать немного Java кода, если нжуно работать с системными уведомлениями. В общем Qt предоставляет очень мощные кроссплатформенные средства для разработки, но и его возможности небезграничны.

спасибо за пояснение.


Only authorized users can post comments.
Please, Log in or Sign up

Hello, Dear Users of EVILEG!!!

If the site helped you, then support the development of the site financially, please.

You can do it by following ways:

Thank you, Evgenii Legotckoi

March 29, 2020, 12:14 p.m.

C++ - Тест 003. Условия и циклы

  • Result:71points,
  • Rating points1
March 29, 2020, 12:05 p.m.

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:80points,
  • Rating points4
March 28, 2020, 3:13 p.m.
Denis Erokhin

C++ - Test 005. Structures and Classes

  • Result:100points,
  • Rating points10
Last comments
March 27, 2020, 2:40 p.m.
Evgenij Legotskoj

Добрый день. В конце пятой статьи скачать можете.
March 27, 2020, 2:28 p.m.
mkdir _

Здравствуйте, а можно, пожалуйста, ссылку на целые исходники, если есть?
March 27, 2020, 4:36 a.m.
Evgenij Legotskoj

Скорее всего также, как и для установки всех остальных переменых в CMake, через использование set
March 27, 2020, 2:47 a.m.
Andrej Jankovich

Ошибка с ярлыками в Windows исправлена в версиии
March 26, 2020, 1:37 p.m.
Andrej Jankovich

но появятся ярлыки, сейчас поправим
Now discuss on the forum
March 30, 2020, 4:16 a.m.
Evgenij Legotskoj

Попробуйте запустить через плейлист, а не через setMedia. Пример есть в этой статье
March 30, 2020, 3:25 a.m.
Evgenij Legotskoj

Добрый день. Почитайте документацию, в исходниках этой библиотеки есть исходнные классы логгеров. Вам наверняка достаточно только правильно вызвать пару функций, чтобы задать имя файла…
March 30, 2020, 3:17 a.m.
Evgenij Legotskoj

Нет, не нужно ничего умножать, если у вас включена поддержа High DPI в приложении а QML, то достаточно держать в памяти, что все значения задаются в DPI, и просто задаёте значения равные DPI сра…
March 27, 2020, 10 a.m.

Оказывается нужно сделать столбец уникальным ALTER TABLE public.drivers ADD UNIQUE (agregator1_id); Потом так работает INSERT INTO drivers( name_driver, surname, middle_name, agr…
March 27, 2020, 8:25 a.m.

Спасибо за ответы. Скорее всего оставлю на неопределенное время под macOS. Хоть будет полезно другим начинающим разработчикам.
© EVILEG 2015-2019
Recommend hosting TIMEWEB