- 1. Знищення на основі CallBack функції
- 2. target.h
- 3. target.cpp
- 4. bullet.h
- 5. bullet.cpp
- 6. віджет.h
- 7. widget.cpp
- 8. Підсумок
- 9. Відеоурок
У двох попередніх статтях, де ми навчили героя відслідковувати переміщення курсору та стріляти в напрямку мети , настав час додати в гру мішені та почати їх знищувати. Знищення мішеней відбуватиметься тоді, коли у мішеней закінчиться життя. При цьому кожна з мішеней матиме випадкову кількість очок життя, а кожна куля завдаватиме випадкової кількості втрат. Також у кожної мішені буде смужка життя, яка зменшуватиметься при нанесенні шкоди.
Знищення на основі CallBack функції
Для реалізації даного алгоритму створимо клас мішені Target , а також додамо до класу Bullet можливість виклику CallBack функції , яка буде реалізована в класі головного вікна програми і буде завдавати шкоди мішеням.
target.h
У заголовному файлі необхідно оголосити функцію, яка буде завдавати шкоди мішені. А також оголосимо дві змінні, які відповідатимуть за здоров'я мішеней. Перша змінна – це буде поточне здоров'я, а друга змінна – це буде максимальне здоров'я. Коли здоров'я закінчується, відбувається знищення мішені.
#ifndef TARGET_H #define TARGET_H #include <QObject> #include <QGraphicsItem> #include <QPainter> class Target : public QObject, public QGraphicsItem { Q_OBJECT public: explicit Target(QObject *parent = 0); ~Target(); /* Функция по нанесению урона, * величина урона передаётся в качестве аргумента функции * */ void hit(int damage); signals: public slots: protected: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); private: int health; // Текущий запас здоровья мишени int maxHealth; // Максимальный запас здоровья мишени }; #endif // TARGET_H
target.cpp
У конструкторі класу відбувається встановлення параметрів здоров'я. У функції нанесення шкоди відбувається зменшення здоров'я на передану цю функцію величину. А щойно здоров'я падає до нуля і нижче, то мета знищується.
#include "target.h" /* Функция для получения рандомного числа * в диапазоне от минимального до максимального * */ static int randomBetween(int low, int high) { return (qrand() % ((high + 1) - low) + low); } Target::Target(QObject *parent) : QObject(parent), QGraphicsItem() { health = randomBetween(1,15); // Задаём случайное значение здоровья maxHealth = health; // Устанавливаем максимальное здоровье равным текущему } Target::~Target() { } QRectF Target::boundingRect() const { return QRectF(-20,-20,40,40); // Ограничиваем область, в которой лежит цель } void Target::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { /* Отрисовываем зеленый квадрат * */ painter->setPen(Qt::black); painter->setBrush(Qt::green); painter->drawRect(-20,-10,40,30); /* Отрисовываем полоску жизни * соизмеримо текущему здоровью * относительно максимального здоровья * */ painter->setPen(Qt::NoPen); painter->setBrush(Qt::red); painter->drawRect(-20,-20, (int) 40*health/maxHealth,3); Q_UNUSED(option); Q_UNUSED(widget); } void Target::hit(int damage) { health -= damage; // Уменьшаем здоровье мишени this->update(QRectF(-20,-20,40,40)); // Перерисовываем мишень // Если здоровье закончилось, то инициируем смерть мишени if(health <= 0) this->deleteLater(); }
bullet.h
До класу кулі з минулого уроку необхідно додати оголошення сигнатури CallBack функції, а також функцію встановлення CallBack функції.
public: // Установка CallBack функции void setCallbackFunc(void (*func) (QGraphicsItem * item)); private: // Объявляем сигнатуру CallBack функции void (*callbackFunc)(QGraphicsItem * item);
bullet.cpp
Також необхідно модифікувати функцію slotTimerBullet , в якій відбуватиметься пошук усіх об'єктів, на які натрапила куля. Якщо куля наткнулася на об'єкт, то знищуємо кулю і викликаємо CallBack функцію, яка буде завдавати шкоди Мішеням, якщо куля наткнулася на мішень.
Також реалізуємо функцію setCallbackFunc , яка здійснить установку вказівника на функцію CallBack функцію .
void Bullet::slotTimerBullet() { setPos(mapToParent(0, -10)); /* Производим проверку на то, наткнулась ли пуля на какой-нибудь * элемент на графической сцене. * Для этого определяем небольшую область перед пулей, * в которой будем искать элементы * */ QList<QGraphicsItem *> foundItems = scene()->items(QPolygonF() << mapToScene(0, 0) << mapToScene(-1, -1) << mapToScene(1, -1)); /* После чего проверяем все элементы. * Одними из них будут сама Пуля и Герой - с ними ничего не делаем. * А с остальными вызываем CallBack функцию * */ foreach (QGraphicsItem *item, foundItems) { if (item == this || item == hero) continue; 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(); } } void Bullet::setCallbackFunc(void (*func)(QGraphicsItem *)) { callbackFunc = func; }
віджет.h
У заголовний файл класу головного вікна необхідно додати оголошення таймера для створення мішеней, а також слота для обробки даного таймера, в якому будуть створювати мішені. Також оголошуємо static список мішеней, який ми перевірятимемо на влучення кулі. А перевірку влучення будемо проводити в CallBack функції slotHitTarget. Як аргумент передаватиметься графічний об'єкт, на який натрапила куля.
private: QTimer *timerTarget; // Таймер для создания мишеней static QList<QGraphicsItem *> targets; // Список мишеней static void slotHitTarget(QGraphicsItem *item); // CallBack Функция private slots: void slotCreateTarget(); // Слот для создания мишеней
widget.cpp
#include "widget.h" #include "ui_widget.h" /* Функция для получения рандомного числа * в диапазоне от минимального до максимального * */ static int randomBetween(int low, int high) { return (qrand() % ((high + 1) - low) + low); } Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { /* Программный код из предыдущих статей * */ // Инициализируем таймер для создания мишеней timerTarget = new QTimer(); connect(timerTarget, &QTimer::timeout, this, &Widget::slotCreateTarget); timerTarget->start(1500); } Widget::~Widget() { delete ui; } void Widget::slotBullet(QPointF start, QPointF end) { /* Программный код из предыдущих уроков * */ } void Widget::slotCreateTarget() { Target *target = new Target(); // Создаём цель scene->addItem(target); // Помещаем цель в сцену со случайной позицией target->setPos(qrand() % ((500 - 40 + 1) - 40) + 40, qrand() % ((500 - 40 + 1) - 40) + 40); target->setZValue(-1); // Помещаем цель ниже Героя targets.append(target); // Добавляем цель в список } void Widget::slotHitTarget(QGraphicsItem *item) { /* Получив сигнал от Пули * Перебираем весь список целей и наносим ему случайный урон * */ foreach (QGraphicsItem *targ, targets) { if(targ == item){ // Кастуем объект из списка в класс Target Target *t = qgraphicsitem_cast <Target *> (targ); t->hit(randomBetween(1,3)); // Наносим урон } } } QList<QGraphicsItem *> Widget::targets; // реализация списка
Підсумок
В результаті у Вас на ігровому полі будуть випадково розміщуватися мішені з випадковим розміром здоров'я, а кулі, що випускаються головним героєм, будуть поступово їх знищувати. У відеоуроці продемонстровано роботу програми, а також додатково подано коментарі за програмним кодом.
Почему то у меня, на строке вызова callback функции, вылетает программа. ((( п.с. Qt 5.7 MSVC 64
e:\work\qt_work\gamedev\les_2\mygame2\bullet.cpp:85: ошибка: Exception at 0x7ff6d35d80b3, code: 0xc0000005: read access violation at: 0x0, flags=0x0 (first chance) e:\work\qt_work\gamedev\les_2\mygame2\bullet.cpp:85: ошибка: Exception at 0x7ff6d35d80b3, code: 0xc0000005: read access violation at: 0x0, flags=0x0 Вот такой exeption вылетает при нажатии мышки (то есть при стрельбе)
Нашел в чем проблема. Вообще в этих уроках, лучше выкладывать весь код каждого файла, а не только ту часть, которая отличается от предыдущего урока. В определении отличий кода между уроками, Вы делаете ошибки)))) Вот и приходится самому думать, чего еще не хватает в коде=)
И так, главный вопрос по этому уроку у меня такой: зачем мы используем callback-функцию, вместо слота+сигнала?
Наверное, это прозвучит странно, но просто так. Чтобы сделать через callback-функцию . Чтобы показать один из возможных вариантов работы в Qt/C++. Случается же так, что те, кто изучает Qt и даже работают с ним некоторое время, не имеют представления о callback-функциях.
И еще для чего нужна конструкция: foreach если есть эквивалент for( : )
И к верхнему посту AndreyHudz → AndreyHudz не надо весь код выкладывать, а лучше сделать преднамеренные ошибки.
да, foreach - это Qt-шный макрос, который эквивалентен for, который появился позже чем foreach.
Я длительное время работал с foreach, пока не решил заняться плотнее новыми стандартами C++ :-)
Поэтому в пятом уроке есть исходники всего проекта )))).
Не подскажите в чем проблема. При нажатии аварийно выходит из программы. Не могу додуматься.
Скачайте просто из пятого урока полностью готовый пример.
А откуда взялся hero? Никак не могу понят секрет его происхождения...
Сам уже не помню. 5 лет назад говнокодил это )) В 5-й части есть полный код, думаю, что там найдёте ))