Алдыңғы екі мақалада біз кейіпкерге курсордың қозғалысын және [нысана бағытында атуды] үйреткен болатынбыз (https://evileg.com /kz/post/ 104/) , ойынға нысандарды қосып, оларды жоюды бастау уақыты келді. Нысаналардың өмірі таусылғанда нысаналар жойылады. Бұл жағдайда нысаналардың әрқайсысы соққы нүктелерінің кездейсоқ санына ие болады және әрбір оқ кездейсоқ мөлшерде зиян келтіреді. Сондай-ақ, әрбір нысанаға зиян келтірілген кезде азаятын өмір жолағы болады.
Қайта шақыру функциясына негізделген жою
Бұл алгоритмді жүзеге асыру үшін біз Target мақсатты класын жасаймыз, сонымен қатар Bullet ішіне CallBack функциясын шақыру мүмкіндігін қосамыз. қосымшаның негізгі терезесінің сыныбында жүзеге асырылатын және нысанаға зиян келтіретін сынып.
мақсатты.сағ
Тақырып файлында мақсатқа зақым келтіретін функцияны жариялау керек. Біз сондай-ақ мақсаттардың денсаулығына жауап беретін екі айнымалыны жариялаймыз. Бірінші айнымалы сіздің ағымдағы денсаулығыңыз болады, ал екінші айнымалы сіздің максималды денсаулығыңыз болады. Денсаулық біткенде, нысана жойылады.
#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 функциясын өзгерту қажет. Егер оқ нысанға тисе, онда біз оқты жойып, Кері шақыру функциясын шақырамыз, ол оқ нысанаға тиген жағдайда Мақсаттарды зақымдайды.
Біз сондай-ақ 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
Негізгі терезе сыныбының тақырып файлында мақсаттарды құруға арналған таймер туралы мәлімдемені, сондай-ақ осы таймерді өңдеуге арналған ұяшықты қосу керек, онда мақсаттар жасалады. Сондай-ақ біз оқ тигендерді тексеретін статикалық нысандар тізімін жариялаймыз. Ал соққыны тексеру slotHitTarget функциясының CallBack ішінде орындалады. Аргумент ретінде, оқ тиген графикалық нысан аргумент ретінде беріледі.
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-й части есть полный код, думаю, что там найдёте ))