Evgenii Legotckoi
Oct. 1, 2015, 8:55 p.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.

  1. #ifndef CUSTOMSCENE_H
  2. #define CUSTOMSCENE_H
  3.  
  4. #include <QObject>
  5. #include <QGraphicsScene>
  6. #include <QGraphicsSceneMouseEvent>
  7. #include <QDebug>
  8.  
  9. class CustomScene : public QGraphicsScene
  10. {
  11. Q_OBJECT
  12. public:
  13. explicit CustomScene(QObject *parent = 0);
  14. ~CustomScene();
  15.  
  16. signals:
  17. // The signal to transmit the coordinates of the mouse position
  18. void signalTargetCoordinate(QPointF point);
  19. void signalShot(bool shot); // Сигнал на стрельбу
  20.  
  21. public slots:
  22.  
  23. private:
  24. // The function, which made tracking the mouse
  25. void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
  26. void mousePressEvent(QGraphicsSceneMouseEvent *event);
  27. void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
  28. };
  29.  
  30. #endif // CUSTOMSCENE_H

customscene.cpp

  1. #include "customscene.h"
  2.  
  3. CustomScene::CustomScene(QObject *parent) :
  4. QGraphicsScene()
  5. {
  6. Q_UNUSED(parent);
  7. }
  8.  
  9. CustomScene::~CustomScene()
  10. {
  11.  
  12. }
  13.  
  14. void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  15. {
  16. emit signalTargetCoordinate(event->scenePos());
  17. }
  18.  
  19. void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
  20. {
  21. emit signalShot(true); // When the mouse button is pressed, it is possible to shoot
  22. Q_UNUSED(event);
  23. }
  24.  
  25. void CustomScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
  26. {
  27. emit signalShot(false); // When the mouse button was released, you can not shoot
  28. Q_UNUSED(event);
  29. }

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.

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3.  
  4. #include <QWidget>
  5. #include <QGraphicsScene>
  6. #include <QGraphicsItem>
  7.  
  8. #include <triangle.h>
  9. #include <customscene.h>
  10. #include <bullet.h>
  11.  
  12. namespace Ui {
  13. class Widget;
  14. }
  15.  
  16. class Widget : public QWidget
  17. {
  18. Q_OBJECT
  19.  
  20. public:
  21. explicit Widget(QWidget *parent = 0);
  22. ~Widget();
  23.  
  24. private:
  25. Ui::Widget *ui;
  26. CustomScene *scene;
  27. Triangle *triangle;
  28.  
  29. private slots:
  30. void slotBullet(QPointF start, QPointF end);
  31. };
  32.  
  33. #endif // WIDGET_H

widget.cpp

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3.  
  4. Widget::Widget(QWidget *parent) :
  5. QWidget(parent),
  6. ui(new Ui::Widget)
  7. {
  8. /* Source code from previous lesson
  9. */
  10. connect(scene, &CustomScene::signalShot, triangle, &Triangle::slotShot);
  11. connect(triangle, &Triangle::signalBullet, this, &Widget::slotBullet);
  12. }
  13.  
  14. Widget::~Widget()
  15. {
  16. delete ui;
  17. }
  18.  
  19. void Widget::slotBullet(QPointF start, QPointF end)
  20. {
  21. // Добавляем на сцену пулю
  22. scene->addItem(new Bullet(start, end));
  23. }

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.

  1. #ifndef TRIANGLE_H
  2. #define TRIANGLE_H
  3.  
  4. #include <QObject>
  5. #include <QGraphicsItem>
  6. #include <QPainter>
  7. #include <QPolygon>
  8. #include <QTimer>
  9. #include <QDebug>
  10. #include <QCursor>
  11.  
  12. #include <windows.h>
  13.  
  14. class Triangle : public QObject, public QGraphicsItem
  15. {
  16. Q_OBJECT
  17. public:
  18. explicit Triangle(QObject *parent = 0);
  19. ~Triangle();
  20.  
  21. signals:
  22. // Signal to produce a bullet trajectory parameters
  23. void signalBullet(QPointF start, QPointF end);
  24.  
  25. public slots:
  26. // The slot for the cursor position data
  27. void slotTarget(QPointF point);
  28. // slot for processing the shooting permission
  29. void slotShot(bool shot);
  30.  
  31. private:
  32. QRectF boundingRect() const;
  33. void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
  34.  
  35. private slots:
  36. void slotGameTimer();
  37. void slotBulletTimer();
  38.  
  39. private:
  40. bool shot;
  41. QTimer *bulletTimer;
  42. QTimer *gameTimer;
  43. QPointF target;
  44. };
  45.  
  46. #endif // TRIANGLE_H

triangle.cpp

  1. #include "triangle.h"
  2. #include <math.h>
  3.  
  4. static const double Pi = 3.14159265358979323846264338327950288419717;
  5. static double TwoPi = 2.0 * Pi;
  6.  
  7. static qreal normalizeAngle(qreal angle)
  8. {
  9. while (angle < 0)
  10. angle += TwoPi;
  11. while (angle > TwoPi)
  12. angle -= TwoPi;
  13. return angle;
  14. }
  15.  
  16. Triangle::Triangle(QObject *parent) :
  17. QObject(parent), QGraphicsItem()
  18. {
  19. setRotation(0);
  20.  
  21. target = QPointF(0,0);
  22. shot = false;
  23.  
  24. gameTimer = new QTimer(); // Init Game Timer
  25. // Connect the signal from the timer and the slot game processing timer
  26. connect(gameTimer, &QTimer::timeout, this, &Triangle::slotGameTimer);
  27. gameTimer->start(10); // Start Timer
  28.  
  29. bulletTimer = new QTimer(); // Init timer of bullets creation
  30. connect(bulletTimer, &QTimer::timeout, this, &Triangle::slotBulletTimer);
  31. bulletTimer->start(1000/6); // Shot 6 times per second
  32.  
  33.  
  34. }
  35.  
  36. Triangle::~Triangle()
  37. {
  38.  
  39. }
  40.  
  41. QRectF Triangle::boundingRect() const
  42. {
  43. return QRectF(-20,-30,40,60);
  44. }
  45.  
  46. void Triangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
  47. {
  48. QPolygon polygon;
  49. polygon << QPoint(0,-30) << QPoint(20,30) << QPoint(-20,30);
  50. painter->setBrush(Qt::red);
  51. painter->drawPolygon(polygon);
  52.  
  53. Q_UNUSED(option);
  54. Q_UNUSED(widget);
  55. }
  56.  
  57. void Triangle::slotTarget(QPointF point)
  58. {
  59. /* Source code from previous lesson */
  60. }
  61.  
  62. void Triangle::slotGameTimer()
  63. {
  64. /* Source code from previous lesson */
  65. }
  66.  
  67. void Triangle::slotBulletTimer()
  68. {
  69. // If shooting is enabled, the call signal to the creation of a bullet
  70. if(shot) emit signalBullet(QPointF(this->x(),this->y()), target);
  71.  
  72. }
  73.  
  74. void Triangle::slotShot(bool shot)
  75. {
  76. this->shot = shot; // We get a permit or deny for shooting
  77. }

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.

  1. #ifndef BULLET_H
  2. #define BULLET_H
  3.  
  4. #include <QObject>
  5. #include <QGraphicsItem>
  6. #include <QTimer>
  7. #include <QPainter>
  8.  
  9. class Bullet : public QObject, public QGraphicsItem
  10. {
  11. Q_OBJECT
  12. public:
  13. explicit Bullet(QPointF start, QPointF end, QObject *parent = 0);
  14. ~Bullet();
  15.  
  16. signals:
  17.  
  18.  
  19. public slots:
  20.  
  21. private:
  22. QRectF boundingRect() const;
  23. void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
  24.  
  25. private:
  26. QTimer *timerBullet;
  27.  
  28. private slots:
  29. void slotTimerBullet();
  30. };
  31.  
  32. #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.

  1. #include "bullet.h"
  2. #include <math.h>
  3.  
  4. static const double Pi = 3.14159265358979323846264338327950288419717;
  5. static double TwoPi = 2.0 * Pi;
  6.  
  7. static qreal normalizeAngle(qreal angle)
  8. {
  9. while (angle < 0)
  10. angle += TwoPi;
  11. while (angle > TwoPi)
  12. angle -= TwoPi;
  13. return angle;
  14. }
  15.  
  16. Bullet::Bullet(QPointF start, QPointF end, QObject *parent)
  17. : QObject(parent), QGraphicsItem()
  18. {
  19. this->setRotation(0); // Set start rotation
  20. this->setPos(start); // Set start position
  21. // Determine the trajectory of the bullet flight
  22. QLineF lineToTarget(start, end);
  23. // The angle of rotation in the direction to the target
  24. qreal angleToTarget = ::acos(lineToTarget.dx() / lineToTarget.length());
  25. if (lineToTarget.dy() < 0)
  26. angleToTarget = TwoPi - angleToTarget;
  27. angleToTarget = normalizeAngle((Pi - angleToTarget) + Pi / 2);
  28.  
  29. /* Expand bullet trajectory
  30. * */
  31. if (angleToTarget >= 0 && angleToTarget < Pi) {
  32. /// Rotate left
  33. setRotation(rotation() - angleToTarget * 180 /Pi);
  34. } else if (angleToTarget <= TwoPi && angleToTarget > Pi) {
  35. /// Rotate right
  36. setRotation(rotation() + (angleToTarget - TwoPi )* (-180) /Pi);
  37. }
  38. // Initialize Flight Timer
  39. timerBullet = new QTimer();
  40. connect(timerBullet, &QTimer::timeout, this, &Bullet::slotTimerBullet);
  41. timerBullet->start(7);
  42. }
  43.  
  44. Bullet::~Bullet()
  45. {
  46.  
  47. }
  48.  
  49. QRectF Bullet::boundingRect() const
  50. {
  51. return QRectF(0,0,2,4);
  52. }
  53.  
  54. void Bullet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
  55. {
  56. painter->setPen(Qt::black);
  57. painter->setBrush(Qt::black);
  58. painter->drawRect(0,0,2,4);
  59.  
  60. Q_UNUSED(option);
  61. Q_UNUSED(widget);
  62. }
  63.  
  64. void Bullet::slotTimerBullet()
  65. {
  66. setPos(mapToParent(0, -10));
  67.  
  68. /* Check the output of the field boundary
  69.      * If the bullet flies over the specified limits the bullet must be destroyed
  70. * */
  71. if(this->x() < 0){
  72. this->deleteLater();
  73. }
  74. if(this->x() > 500){
  75. this->deleteLater();
  76. }
  77.  
  78. if(this->y() < 0){
  79. this->deleteLater();
  80. }
  81. if(this->y() > 500){
  82. this->deleteLater();
  83. }
  84. }

Result

As a result, our protagonist has got the ability to shoot. What is particularly shown in the video tutorial on this article.

Video

Do you like it? Share on social networks!

k
  • Feb. 7, 2018, 12:41 a.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. 7, 2018, 4:19 a.m.

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

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

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

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

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

Comments

Only authorized users can post comments.
Please, Log in or Sign up
  • Last comments
  • AK
    April 1, 2025, 11:41 a.m.
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    March 9, 2025, 9:02 p.m.
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    March 9, 2025, 4:14 p.m.
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
  • ИМ
    Nov. 22, 2024, 9:51 p.m.
    Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
  • Evgenii Legotckoi
    Oct. 31, 2024, 11:37 p.m.
    Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup