- 1. Уничтожение на основе CallBack функции
- 2. target.h
- 3. target.cpp
- 4. bullet.h
- 5. bullet.cpp
- 6. widget.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;
- }
widget.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-й части есть полный код, думаю, что там найдёте ))