In den beiden vorherigen Artikeln, in denen wir dem Helden beigebracht haben, Cursorbewegungen zu verfolgen und in Richtung des Ziels zu schießen , ist es an der Zeit, dem Spiel Ziele hinzuzufügen und sie zu zerstören. Die Zerstörung von Zielen tritt ein, wenn die Leben der Ziele ausgehen. In diesem Fall hat jedes der Ziele eine zufällige Anzahl von Trefferpunkten und jede Kugel verursacht einen zufälligen Schaden. Außerdem hat jedes Ziel einen Lebensbalken, der mit zunehmendem Schaden abnimmt.
Zerstörung basierend auf CallBack-Funktion
Um diesen Algorithmus zu implementieren, erstellen wir eine Zielklasse Target und fügen der Klasse Bullet außerdem die Möglichkeit hinzu, die CallBack-Funktion aufzurufen, die in der Klasse des Hauptanwendungsfensters implementiert wird und Ziele beschädigt.
target.h
In der Header-Datei müssen Sie eine Funktion deklarieren, die das Ziel beschädigt. Außerdem werden wir zwei Variablen deklarieren, die für die Gesundheit der Ziele verantwortlich sind. Die erste Variable ist Ihre aktuelle Gesundheit und die zweite Variable ist Ihre maximale Gesundheit. Wenn die Gesundheit endet, wird das Ziel zerstört.
#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
Die Integritätsparameter werden im Klassenkonstruktor festgelegt. In der Funktion Schaden zufügen wird die Gesundheit um den auf diese Funktion übertragenen Wert reduziert. Und sobald die Gesundheit auf null und darunter sinkt, wird das Ziel zerstört.
#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
In der Aufzählungsklasse aus der vorherigen Lektion müssen Sie Deklarationen der Signatur der CallBack-Funktion sowie eine Funktion zum Festlegen der CallBack-Funktion hinzufügen.
public: // Установка CallBack функции void setCallbackFunc(void (*func) (QGraphicsItem * item)); private: // Объявляем сигнатуру CallBack функции void (*callbackFunc)(QGraphicsItem * item);
bullet.cpp
Es ist auch notwendig, die Funktion slotTimerBullet zu ändern, die nach allen Objekten sucht, über die die Kugel gestolpert ist. Wenn die Kugel das Objekt trifft, zerstören wir die Kugel und rufen die CallBack -Funktion auf, die die Ziele beschädigt, wenn die Kugel das Ziel trifft.
Wir implementieren auch die Funktion setCallbackFunc , die den Funktionszeiger auf die [CallBack-Funktion] setzt (https://evileg.com/ru/post/89/).
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
In der Header-Datei der Hauptfensterklasse müssen Sie eine Timer-Deklaration zum Erstellen von Zielen sowie einen Slot für die Verarbeitung dieses Timers hinzufügen, in dem Ziele erstellt werden. Wir deklarieren auch eine statische Liste von Zielen, die wir auf Kugeltreffer überprüfen. Und die Trefferprüfung wird in CallBack der Funktion slotHitTarget durchgeführt Das grafische Objekt, das der Kugeltreffer als Argument übergeben wird.
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; // реализация списка
Ergebnis
Als Ergebnis werden Ziele mit einer zufälligen Gesundheitsgröße zufällig auf dem Spielfeld platziert und die vom Protagonisten abgefeuerten Kugeln werden sie nach und nach zerstören. Das Video-Tutorial demonstriert die Bedienung der Anwendung und bietet zusätzliche Kommentare zum Programmcode.
Почему то у меня, на строке вызова 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-й части есть полный код, думаю, что там найдёте ))