- 1. Структура проекту з класом Bullet
- 2. customscene.h
- 3. customscene.cpp
- 4. віджет.h
- 5. widget.cpp
- 6. трикутник.h
- 7. triangle.cpp
- 8. bullet.h
- 9. bullet.cpp
- 10. Підсумок
- 11. Відеоурок
Після того, як Ми почали керувати своїм героєм, і його погляд завжди звернений у бік мети, настав час написати клас Bullet , який відповідатиме за кулі та їхній політ по ігровій сцені. Механіка переміщення кулі за графічною сценою буде аналогічна механіці переміщення головного героя. Відмінність буде в тому, що куля завжди рухається по прямій і розворот кулі потрібно буде встановити тільки в момент створення об'єкта класу **Bullet, щоб задати напрямки польоту кулі.
Структура проекту з класом Bullet
Структура проекту з попереднього уроку змінюється в тому плані, що додається новий клас Bullet. Також потрібно доопрацювати всі інші класи для забезпечення взаємодії з новим класом .
Тому почнемо по порядку, а саме з класу, де ініціалізується подія, що викликає процес стрілянини.
customscene.h
У кастомізованій графічній сцені необхідно буде додати функції для обробки натискань клавіш миші ( mousePressEvent і mouseReleaseEvent ), а також сигнал передачі дозволу на стрілянину від графічної сцени, до головного героя. Звичайно, перевіряти натискання клавіш миші можна було б і в класі головного героя, але проблема полягає в тому, що потрібно дозволити стрілянину тільки в області графічної сцени, оскільки її можуть бути інші інтерактивні елементи, які не повинні викликати події стрілянини.
- #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:
- // Сигнал для передачи координат положения курсора мыши
- void signalTargetCoordinate(QPointF point);
- void signalShot(bool shot); // Сигнал на стрельбу
- public slots:
- private:
- // Функция, в которой производится отслеживание положения мыши
- 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); // Когда клавиша мыши нажата, то можно стрелять
- Q_UNUSED(event);
- }
- void CustomScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
- {
- emit signalShot(false); // Когда клавишу мыши отпустили, то стрелять нельзя
- Q_UNUSED(event);
- }
віджет.h
Для передачі даних про дозвіл стрільби підключаємо signalShot із графічної сцени до slotShot у трикутнику. У ньому буде передаватися інформація про те, чи натиснуто клавішу миші в області графічної сцени. Також із трикутника передається signalBullet, який ініціює створення кулі, в ядро гри, у клас Widget. У слоті slotBullet створюється об'єкт класу Bullet та встановлюється на графічну сцену.
- #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)
- {
- /* Программный код из урока
- * GameDev. Отслеживание перемещения мыши в QGraphicsScene
- */
- // Соединяем сигнала стрельбы с графической сцены со слотом разрешения стрельбы треугольника
- 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));
- }
трикутник.h
У класі головного героя необхідно створити новий таймер, який ініціюватиме створення нової кулі 6 разів на секунду, якщо надійшов сигнал із графічної сцени на дозвіл стрілянини. Як тільки надійшов сигнал на припинення стрілянини, створення куль ігнорується.
- #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:
- // Сигнал для создания пули с параметрами траектории
- void signalBullet(QPointF start, QPointF end);
- public slots:
- // Слот для получения данных о положении курсора
- void slotTarget(QPointF point);
- // слот для обработки разрешения стрельбы
- 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(); // Инициализируем игровой таймер
- // Подключаем сигнал от таймера и слоту обработки игрового таймера
- connect(gameTimer, &QTimer::timeout, this, &Triangle::slotGameTimer);
- gameTimer->start(10); // Стартуем таймер
- bulletTimer = new QTimer(); // Инициализируем таймер создания пуль
- connect(bulletTimer, &QTimer::timeout, this, &Triangle::slotBulletTimer);
- bulletTimer->start(1000/6); // Стреляем 6 раз в секунду
- }
- 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)
- {
- /* Программный код из урока
- * GameDev. Отслеживание перемещения мыши в QGraphicsScene
- */
- }
- void Triangle::slotGameTimer()
- {
- /* Программный код из урока
- * GameDev. Отслеживание перемещения мыши в QGraphicsScene
- */
- }
- void Triangle::slotBulletTimer()
- {
- // Если стрельба разрешена, то вызываем сигнал на создание пули
- if(shot) emit signalBullet(QPointF(this->x(),this->y()), target);
- }
- void Triangle::slotShot(bool shot)
- {
- this->shot = shot; // Получаем разрешение или запрет на стрельбу
- }
bullet.h
А тепер приступимо до створення самої кулі за допомогою класу Bullet . Цей клас успадковується від QGraphicsItem і робота з ним проводиться аналогічно до роботи з головним героєм. При виході кулі за межі ігрової області вона повинна бути знищена для звільнення пам'яті програми.
- #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
У конструктор кулі вдається два об'єкти QPointF, які задають траєкторію польоту кулі. Перша координата - це місце вильоту кулі, місце, де ініційовано її створення, і друга координата - це місцезнаходження курсору на момент пострілу. За цими двома координатами задається траєкторія її польоту.
Ігровий таймер кулі по тиках ініціює запуск ігрового слота, в якому куля просувається на 10 пікселів уперед і у разі виходу за межі ігрової області знищується.
- #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); // Устанавливаем начальный разворот
- this->setPos(start); // Устанавливаем стартовую позицию
- // Определяем траекторию полёта пули
- QLineF lineToTarget(start, end);
- // Угол поворота в направлении к цели
- qreal angleToTarget = ::acos(lineToTarget.dx() / lineToTarget.length());
- if (lineToTarget.dy() < 0)
- angleToTarget = TwoPi - angleToTarget;
- angleToTarget = normalizeAngle((Pi - angleToTarget) + Pi / 2);
- /* Разворачиваем пули по траектории
- * */
- 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);
- }
- // Инициализируем полётный таймер
- 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));
- /* Проверка выхода за границы поля
- * Если пуля вылетает за заданные границы, то пулю необходимо уничтожить
- * */
- if(this->x() < 0){
- this->deleteLater();
- }
- if(this->x() > 500){
- this->deleteLater();
- }
- if(this->y() < 0){
- this->deleteLater();
- }
- if(this->y() > 500){
- this->deleteLater();
- }
- }
Підсумок
В результаті наш головний герой обзавівся здатністю стріляти. Що докладно продемонстровано у відеоуроці за цією статтею. У відеоуроці також подано коментарі за програмним кодом.
почему-то при компиляции ругается на Bullet тут
Вы переопределили методы paint и boundingRect?
Все оказалось куда проще: не поставил const перед QStyleOptionGraphicsItem *option как в заголовочном файле, так и в фале исходного кода.
Наиболее удобный интерфейс и управление у CLion, однако он не имеет поддержки синтаксиса и макросов Qt, что очень сильно тормозит скорость разработки, по сравнению с Qt Creator, а также абсолютно нет поддержки QML, кроме синтаксиса с помощью плагина.