Evgenii Legotckoi
Evgenii LegotckoiOct. 19, 2015, 1:23 p.m.

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

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

Video

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.

Do you like it? Share on social networks!

L
  • May 16, 2017, 10:38 a.m.

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

L
  • May 16, 2017, 10:41 a.m.

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

void Widget::slotBullet(QPointF start, QPointF end)
{
    /// Добавляем на сцену пулю
    Bullet *bullet = new Bullet(start, end, triangle);
    bullet->setCallbackFunc(slotHitTarget);
    scene->addItem(bullet);
}
Evgenii Legotckoi
  • May 16, 2017, 11:13 a.m.

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

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

This scene takes ownership of the item.

Следовательно, учитывая специфику Qt фреймворка, что parent удаляет своих children при разрушении, можно не беспокоиться об утечке памяти в данном моменте, поскольку графическая сцена при удалении должна будет удалить свои Item`ы.

L
  • May 16, 2017, 1:52 p.m.

Нажимал несколько раз, но страница перезагрузилась один раз. Спасибо за ответ.

Evgenii Legotckoi
  • May 16, 2017, 2:27 p.m.

Комментарии добавляются через 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"))

a
  • May 7, 2018, 1:36 a.m.

Здравствуйте!

Скачал проект чтобы попробовать. Не собирается:
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 бита).

Подскажите, пожалуйста, что нужно сделать?
a
  • May 7, 2018, 1:40 a.m.

Вбил

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

Правильно понимаю, что на Linux работать не будет?
Evgenii Legotckoi
  • May 7, 2018, 5:59 a.m.

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

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

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

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

a
  • May 10, 2018, 12:46 a.m.

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


По поводу клавиш. Думал раньше, что Qt сам реализует платформонезависимые элементы, в т.ч. отслеживание нажатие клавиш. А тут как-то странно получилось.
Evgenii Legotckoi
  • May 10, 2018, 3:47 a.m.

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

"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 предоставляет очень мощные кроссплатформенные средства для разработки, но и его возможности небезграничны.
a
  • May 10, 2018, 5:35 a.m.

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

b
  • Nov. 26, 2022, 3:01 p.m.

Если вдруг кто-то прочитает....
Скачал проект, скомпилил, запустил. Всё красиво и объектно ориентировано, но вот FPS дико страдает, когда появляется 10+ врагов. Может есть какие-то надстройки? Ведь если нет, то в чём вообще практический смысл QGraphicView/Scene/Items, если всё то же самое и даже с более тяжелыми анимацией и физикой с отрисовкой на том же QPainter летает даже на самом дровяном ноутбуке?

Comments

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

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

  • Result:50points,
  • Rating points-4
m

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

  • Result:80points,
  • Rating points4
m

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

  • Result:20points,
  • Rating points-10
Last comments
ИМ
Игорь МаксимовNov. 22, 2024, 10:51 p.m.
Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiNov. 1, 2024, 12:37 a.m.
Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEOct. 19, 2024, 6:19 p.m.
Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовOct. 5, 2024, 5:51 p.m.
Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5July 5, 2024, 9:02 p.m.
QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Now discuss on the forum
Evgenii Legotckoi
Evgenii LegotckoiJune 25, 2024, 1:11 a.m.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Nov. 15, 2024, 5:04 p.m.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectJune 4, 2022, 1:49 p.m.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimOct. 25, 2024, 7:10 p.m.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Follow us in social networks