Evgenii Legotckoi
Evgenii LegotckoiOct. 1, 2015, 10:55 a.m.

GameDev on Qt - Tutorial 2. Bullet Writing class for shooting in Qt

Once we started to manage their hero, and his gaze is always turned toward the goal, it's time to write a class of Bullet, which will be responsible for the bullets and their flight on the gaming scene. The mechanics of moving a bullet on the graphic scene will be similar to the mechanics of movement of the main character. The difference is that the bullet will always move in a straight line and turn the bullets need to be set only at the time of the creation of Bullet class object, to set the direction of flight of the bullet.

Project structure

The structure of the project from the previous lesson is changed in the sense that a new class is added Bullet. You also need to modify all of the other classes to ensure interaction with the new class.

So we start in order, namely, the class which is initialized to the event causing the firing process.


customscene.h

The customized graphics scene will need to add a function to handle a mouse click ( mousePressEvent and mouseReleaseEvent ), as well as a signal for transmission permission for shooting from the graphic scene, the main character. Of course, check the mouse press could be in the class of the hero, but the problem is that you need to allow shooting only in the field of graphic scenes, because it it can be other interactive elements that should not cause a fire event.

#ifndef CUSTOMSCENE_H
#define CUSTOMSCENE_H

#include <QObject>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>

class CustomScene : public QGraphicsScene
{
    Q_OBJECT
public:
    explicit CustomScene(QObject *parent = 0);
    ~CustomScene();

signals:
    // The signal to transmit the coordinates of the mouse position
    void signalTargetCoordinate(QPointF point);
    void signalShot(bool shot); // Сигнал на стрельбу

public slots:

private:
    // The function, which made tracking the mouse
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};

#endif // CUSTOMSCENE_H

customscene.cpp

#include "customscene.h"

CustomScene::CustomScene(QObject *parent) :
    QGraphicsScene()
{
    Q_UNUSED(parent);
}

CustomScene::~CustomScene()
{

}

void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    emit signalTargetCoordinate(event->scenePos());
}

void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    emit signalShot(true); // When the mouse button is pressed, it is possible to shoot
    Q_UNUSED(event);
}

void CustomScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    emit signalShot(false); // When the mouse button was released, you can not shoot
    Q_UNUSED(event);
}

widget.h

To transfer data to permit the firing connect signalShot of graphic scene to slotShot in the triangle. In it will be sent information on whether the mouse button is clicked in the graphic scene. Also in the triangle is passed signalBullet, which triggers the creation of a bullet in the core of the game, in the Widget class. In slotBullet slot created Bullet class object and is set on a graphic scene.

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QGraphicsScene>
#include <QGraphicsItem>

#include <triangle.h>
#include <customscene.h>
#include <bullet.h>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget *ui;
    CustomScene  *scene;    
    Triangle *triangle;     

private slots:
    void slotBullet(QPointF start, QPointF end);
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    /* Source code from previous lesson
     */
    connect(scene, &CustomScene::signalShot, triangle, &Triangle::slotShot);
    connect(triangle, &Triangle::signalBullet, this, &Widget::slotBullet);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::slotBullet(QPointF start, QPointF end)
{
    // Добавляем на сцену пулю
    scene->addItem(new Bullet(start, end));
}

triangle.h

In the class of the hero, you must create a new timer, which will trigger the creation of a new bullet 6 times per second, if the signal was received from the graphic scene on fire resolution. Once received a signal to stop the shooting, the creation of bullets ignored.

#ifndef TRIANGLE_H
#define TRIANGLE_H

#include <QObject>
#include <QGraphicsItem>
#include <QPainter>
#include <QPolygon>
#include <QTimer>
#include <QDebug>
#include <QCursor>

#include <windows.h>

class Triangle : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit Triangle(QObject *parent = 0);
    ~Triangle();

signals:
    // Signal to produce a bullet trajectory parameters
    void signalBullet(QPointF start, QPointF end);

public slots:
    // The slot for the cursor position data
    void slotTarget(QPointF point);
    // slot for processing the shooting permission
    void slotShot(bool shot);

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

private slots:
    void slotGameTimer();   
    void slotBulletTimer(); 

private:
    bool shot;              
    QTimer *bulletTimer;    
    QTimer *gameTimer;     
    QPointF target;        
};

#endif // TRIANGLE_H

triangle.cpp

#include "triangle.h"
#include <math.h>

static const double Pi = 3.14159265358979323846264338327950288419717;
static double TwoPi = 2.0 * Pi;

static qreal normalizeAngle(qreal angle)
{
    while (angle < 0)
        angle += TwoPi;
    while (angle > TwoPi)
        angle -= TwoPi;
    return angle;
}

Triangle::Triangle(QObject *parent) :
    QObject(parent), QGraphicsItem()
{
    setRotation(0);      

    target = QPointF(0,0); 
    shot = false;

    gameTimer = new QTimer();   // Init Game Timer
    // Connect the signal from the timer and the slot game processing timer
    connect(gameTimer, &QTimer::timeout, this, &Triangle::slotGameTimer);
    gameTimer->start(10);   // Start Timer

    bulletTimer = new QTimer(); // Init timer of bullets creation
    connect(bulletTimer, &QTimer::timeout, this, &Triangle::slotBulletTimer);
    bulletTimer->start(1000/6); // Shot 6 times per second


}

Triangle::~Triangle()
{

}

QRectF Triangle::boundingRect() const
{
    return QRectF(-20,-30,40,60);
}

void Triangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPolygon polygon;
    polygon << QPoint(0,-30) << QPoint(20,30) << QPoint(-20,30);
    painter->setBrush(Qt::red);
    painter->drawPolygon(polygon);

    Q_UNUSED(option);
    Q_UNUSED(widget);
}

void Triangle::slotTarget(QPointF point)
{
    /* Source code from previous lesson */
}

void Triangle::slotGameTimer()
{
   /* Source code from previous lesson */
}

void Triangle::slotBulletTimer()
{
    // If shooting is enabled, the call signal to the creation of a bullet
    if(shot) emit signalBullet(QPointF(this->x(),this->y()), target);

}

void Triangle::slotShot(bool shot)
{
    this->shot = shot;  // We get a permit or deny for shooting
}

bullet.h

Now let's create the most by a bullet Bullet class. This class is inherited from QGraphicsItem and working with it is similar to the work produced with the main character. When you exit the bullet out of the playing field, it must be destroyed to release the program memory.

#ifndef BULLET_H
#define BULLET_H

#include <QObject>
#include <QGraphicsItem>
#include <QTimer>
#include <QPainter>

class Bullet : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit Bullet(QPointF start, QPointF end, QObject *parent = 0);
    ~Bullet();

signals:


public slots:

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

private:
    QTimer *timerBullet;    

private slots:
    void slotTimerBullet(); 
};

#endif // BULLET_H

bullet.cpp

We send two objects QPointF onto the bullet constructor, which define the trajectory of the bullet flight. The first coordinate is - a place of departure bullets, the place where it was initiated by the establishment, and the second coordinate - it is the location of the cursor at the time of the shot. For these two coordinates given by the trajectory of its flight.

Game Bullet timer ticks by initiating the launch of the game slot in which the ball is moving forward and 10 pixels in case of the playing field is destroyed.

#include "bullet.h"
#include <math.h>

static const double Pi = 3.14159265358979323846264338327950288419717;
static double TwoPi = 2.0 * Pi;

static qreal normalizeAngle(qreal angle)
{
    while (angle < 0)
        angle += TwoPi;
    while (angle > TwoPi)
        angle -= TwoPi;
    return angle;
}

Bullet::Bullet(QPointF start, QPointF end, QObject *parent)
    : QObject(parent), QGraphicsItem()
{
    this->setRotation(0);   // Set start rotation
    this->setPos(start);    // Set start position
    // Determine the trajectory of the bullet flight
    QLineF lineToTarget(start, end);
    // The angle of rotation in the direction to the target
    qreal angleToTarget = ::acos(lineToTarget.dx() / lineToTarget.length());
    if (lineToTarget.dy() < 0)
        angleToTarget = TwoPi - angleToTarget;
    angleToTarget = normalizeAngle((Pi - angleToTarget) + Pi / 2);

    /* Expand bullet trajectory
     * */
    if (angleToTarget >= 0 && angleToTarget < Pi) {
        /// Rotate left
        setRotation(rotation() - angleToTarget * 180 /Pi);
    } else if (angleToTarget <= TwoPi && angleToTarget > Pi) {
        /// Rotate right
        setRotation(rotation() + (angleToTarget - TwoPi )* (-180) /Pi);
    }
    // Initialize Flight Timer
    timerBullet = new QTimer();
    connect(timerBullet, &QTimer::timeout, this, &Bullet::slotTimerBullet);
    timerBullet->start(7);
}

Bullet::~Bullet()
{

}

QRectF Bullet::boundingRect() const
{
    return QRectF(0,0,2,4);
}

void Bullet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setPen(Qt::black);
    painter->setBrush(Qt::black);
    painter->drawRect(0,0,2,4);

    Q_UNUSED(option);
    Q_UNUSED(widget);
}

void Bullet::slotTimerBullet()
{
    setPos(mapToParent(0, -10));

    /* Check the output of the field boundary
     * If the bullet flies over the specified limits the bullet must be destroyed
     * */
    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, our protagonist has got the ability to shoot. What is particularly shown in the video tutorial on this article.

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!

k
  • Feb. 6, 2018, 1:41 p.m.

почему-то при компиляции ругается на Bullet тут

  1. void Widget::slotBullet(QPointF start, QPointF end)
  2. {
  3. // Добавляем на сцену пулю
  4. scene->addItem(new Bullet(start, end));
  5. }
ошибка: invalid new-expression of abstract class type 'Bullet'
Почему компилятор считает его абстрактным?
Qt 5.10, компилятор minGW 5.3.0
Evgenii Legotckoi
  • Feb. 6, 2018, 5:19 p.m.

Вы переопределили методы paint и boundingRect?

И возможно, что требуется переопределить ещё какие-то методы в Qt 5.10. Посмотрите, что там есть из виртуальных методов у базового класса bullet.
Конкретно посмотрите методы которые приравнены нулю, например, с такой записью
virtual void someMethod() = 0;
k
  • Feb. 12, 2018, 11:33 a.m.

Все оказалось куда проще: не поставил const перед QStyleOptionGraphicsItem *option как в заголовочном файле, так и в фале исходного кода.

Зато получше познакомился с интерфейсом Qt Creator, пока следовал вашему совету, очень удобный. Спасибо :)
Evgenii Legotckoi
  • Feb. 12, 2018, 4:17 p.m.

Наиболее удобный интерфейс и управление у CLion, однако он не имеет поддержки синтаксиса и макросов Qt, что очень сильно тормозит скорость разработки, по сравнению с Qt Creator, а также абсолютно нет поддержки QML, кроме синтаксиса с помощью плагина.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
e
  • ehot
  • March 31, 2024, 2:29 p.m.

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

  • Result:78points,
  • Rating points2
B

C++ - Test 002. Constants

  • Result:16points,
  • Rating points-10
B

C++ - Test 001. The first program and data types

  • Result:46points,
  • Rating points-6
Last comments
k
kmssrFeb. 8, 2024, 6:43 p.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 10:30 a.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 8:38 a.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 18, 2023, 9:01 p.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
a
a_vlasovApril 14, 2024, 6:41 a.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел ДорофеевApril 14, 2024, 2:35 a.m.
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrexApril 4, 2024, 4:47 a.m.
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
AC
Alexandru CodreanuJan. 19, 2024, 11:57 a.m.
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Follow us in social networks