Evgenii Legotckoi
Evgenii Legotckoi05 жовтня 2015 р. 10:58

GameDev на Qt - Підручник 3. Знищення супротивників

У двох попередніх статтях, де ми навчили героя відслідковувати переміщення курсору та стріляти в напрямку мети , настав час додати в гру мішені та почати їх знищувати. Знищення мішеней відбуватиметься тоді, коли у мішеней закінчиться життя. При цьому кожна з мішеней матиме випадкову кількість очок життя, а кожна куля завдаватиме випадкової кількості втрат. Також у кожної мішені буде смужка життя, яка зменшуватиметься при нанесенні шкоди.

Знищення на основі 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; // реализация списка

Підсумок

В результаті у Вас на ігровому полі будуть випадково розміщуватися мішені з випадковим розміром здоров'я, а кулі, що випускаються головним героєм, будуть поступово їх знищувати. У відеоуроці продемонстровано роботу програми, а також додатково подано коментарі за програмним кодом.

Відеоурок

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

A
  • 05 січня 2017 р. 15:58

Почему то у меня, на строке вызова callback функции, вылетает программа. ((( п.с. Qt 5.7 MSVC 64

A
  • 05 січня 2017 р. 16:04

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 вылетает при нажатии мышки (то есть при стрельбе)

A
  • 05 січня 2017 р. 16:22

Нашел в чем проблема. Вообще в этих уроках, лучше выкладывать весь код каждого файла, а не только ту часть, которая отличается от предыдущего урока. В определении отличий кода между уроками, Вы делаете ошибки)))) Вот и приходится самому думать, чего еще не хватает в коде=)

A
  • 05 січня 2017 р. 16:25

И так, главный вопрос по этому уроку у меня такой: зачем мы используем callback-функцию, вместо слота+сигнала?

Evgenii Legotckoi
  • 05 січня 2017 р. 22:44

Наверное, это прозвучит странно, но просто так. Чтобы сделать через callback-функцию . Чтобы показать один из возможных вариантов работы в Qt/C++. Случается же так, что те, кто изучает Qt и даже работают с ним некоторое время, не имеют представления о callback-функциях.

S
  • 08 липня 2017 р. 15:14
  1. /* После чего проверяем все элементы.
  2. * Одними из них будут сама Пуля и Герой - с ними ничего не делаем.
  3. * А с остальными вызываем CallBack функцию
  4. * */
  5. foreach (QGraphicsItem *item, foundItems) {
  6. if (item == this || item == hero)
  7. continue;
  8. callbackFunc(item); // Вызываем CallBack функцию
  9. this->deleteLater(); // Уничтожаем пулю
  10. }
Проработав Ваш код появился вопрос:  кто такой - hero - в данном исполнителе. Не подключали вроде в предыдущих уроках класс треугольника...решил вопрос с помощью RTTI.
S
  • 08 липня 2017 р. 15:17

И еще для чего нужна конструкция: foreach если есть эквивалент for( : )

S
  • 08 липня 2017 р. 15:29

И к верхнему посту AndreyHudz не надо весь код выкладывать, а лучше сделать преднамеренные ошибки.

Evgenii Legotckoi
  • 08 липня 2017 р. 16:04

да, foreach - это Qt-шный макрос, который эквивалентен for, который появился позже чем foreach.
Я длительное время работал с foreach, пока не решил заняться плотнее новыми стандартами C++ :-)

Evgenii Legotckoi
  • 08 липня 2017 р. 16:07

Поэтому в пятом уроке есть исходники всего проекта )))).

Вообще, все эти материалы были не предыдущей версии сайта, которая на WordPress. Во время переноса мог что-то потерять.
Д
  • 25 жовтня 2018 р. 10:10

Не подскажите в чем проблема. При нажатии аварийно выходит из программы. Не могу додуматься.

Evgenii Legotckoi
  • 25 жовтня 2018 р. 17:17

Скачайте просто из пятого урока полностью готовый пример.

VB
  • 25 червня 2020 р. 12:35

А откуда взялся hero? Никак не могу понят секрет его происхождения...

Evgenii Legotckoi
  • 25 червня 2020 р. 14:36

Сам уже не помню. 5 лет назад говнокодил это )) В 5-й части есть полный код, думаю, что там найдёте ))

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
Дмитрий

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:60бали,
  • Рейтинг балів-1
Дмитрий

C++ - Тест 003. Условия и циклы

  • Результат:92бали,
  • Рейтинг балів8
d
  • dsfs
  • 26 квітня 2024 р. 16:56

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
Останні коментарі
k
kmssr09 лютого 2024 р. 07:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 14:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 23:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 21:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 грудня 2023 р. 10:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
G
George1307 травня 2024 р. 12:27
добавить qlineseries в функции в функции: "GPlotter::addSeries(QString title, QVector &arr)" я вызываю метод setChart(...), я в конструктор передал адрес на QChartView элемент
BlinCT
BlinCT05 травня 2024 р. 17:46
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
PS
Peter Son04 травня 2024 р. 05:57
Best Indian Food Restaurant In Cincinnati OH Ready to embark on a gastronomic journey like no other? Join us at App india restaurant and discover why we're renowned as the Best Indian Food Restaurant In Cincinnati OH . Whether y…
Evgenii Legotckoi
Evgenii Legotckoi03 травня 2024 р. 02:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderChe
IscanderChe30 квітня 2024 р. 16:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…

Слідкуйте за нами в соціальних мережах