- 1. Project structure
- 2. customscene.h
- 3. customscene.cpp
- 4. widget.h
- 5. widget.cpp
- 6. triangle.h
- 7. triangle.cpp
- 8. bullet.h
- 9. bullet.cpp
- 10. Result
- 11. Video
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.
почему-то при компиляции ругается на Bullet тут
Вы переопределили методы paint и boundingRect?
Все оказалось куда проще: не поставил const перед QStyleOptionGraphicsItem *option как в заголовочном файле, так и в фале исходного кода.
Наиболее удобный интерфейс и управление у CLion, однако он не имеет поддержки синтаксиса и макросов Qt, что очень сильно тормозит скорость разработки, по сравнению с Qt Creator, а также абсолютно нет поддержки QML, кроме синтаксиса с помощью плагина.