Реклама

Как написать игру на Qt - Урок 3. Взаимодействие с другими объектами

TutorialQtGameDev, QGraphicsItem, QGraphicsScene, QPainter, Qt, написать игру235

После того, как в игре присутствует анимированная Муха, которая передвигается под воздействием клавиш клавиатуры, настало время добавить смысл в игру. Добавим цель Мухе, например, она будет поедать яблоки, которые будут подсчитываться. То есть необходимо настроить взаимодействие Мухи с другими объектами, в данном случае с яблоками.

Для яблок необходимо создать отдельный класс, который также будет отнаследован от QGraphicsItem. При создании яблоки должны иметь случайный цвет, что также будет инициализироваться в конструкторе класса яблок с помощью функции qrand().

Когда Муха натыкается на яблоко, необходимо передавать данную информацию в ядро игры, которое находится в классе widget. Для этого будем при движении Мухи анализировать объекты, которые попадают в поле её зрения, и с которыми необходимо отработать взаимодействие.

    /* Производим проверку на то, наткнулась ли муха на какой-нибудь
     * элемент на графической сцене.
     * Для этого определяем небольшую область перед мухой,
     * в которой будем искать элементы
     * */
    QList<QGraphicsItem *> foundItems = scene()->items(QPolygonF()
                                                           << mapToScene(0, 0)
                                                           << mapToScene(-20, -20)
                                                           << mapToScene(20, -20));
    /* После чего проверяем все элементы.
     * Один из них будет сама Муха - с ней ничего не делаем.
     * А с остальными высылаем сигнал в ядро игры
     * */
    foreach (QGraphicsItem *item, foundItems) {
        if (item == this)
            continue;
        emit signalCheckItem(item);
    }

Как только Муха обнаружила объект, отличный от неё самой, то передаём его в ядро игры, где игра проверит, что объект является яблоком и удалит его, увеличив счёт игры на единицу.

void Widget::slotDeleteApple(QGraphicsItem *item)
{
    /* Получив сигнал от Мухи
     * Перебираем весь список яблок и удаляем найденное яблоко
     * */
    foreach (QGraphicsItem *apple, apples) {
        if(apple == item){
            scene->removeItem(apple);   // Удаляем со сцены
            apples.removeOne(item);     // Удаляем из списка
            delete apple;               // Вообще удаляем
            ui->lcdNumber->display(count++);    /* Увеличиваем счёт на единицу
                                                 * и отображаем на дисплее
                                                 * */
        }
    }
}

Создание же яблок, с которыми будет происходить взаимодействие должно осуществляться с определённой периодичностью, что будет инициализироваться специальным таймером.

    /* Раз в секунду отсылаем сигнал на создание яблока в игре
     * */
    timerCreateApple = new QTimer();
    connect(timerCreateApple, &QTimer::timeout, this, &Widget::slotCreateApple);
    timerCreateApple->start(1000);

    /* Подключаем сигнал от Мухи, в котором передаются Объекты, на которые
     * наткнулась Муха
     * */
    connect(triangle, &Triangle::signalCheckItem, this, &Widget::slotDeleteApple);

Создание яблока производится в функции slotCreateApple()

void Widget::slotCreateApple()
{
    Apple *apple = new Apple(); // Создаём яблоко
    scene->addItem(apple);      // Помещаем его в сцену со случайной позицией
    apple->setPos((qrand() % (251)) * ((qrand()%2 == 1)?1:-1),
                  (qrand() % (251)) * ((qrand()%2 == 1)?1:-1));
    apple->setZValue(-1);       /* Помещаем яблоко ниже Мухи, то есть Муха
                                 * на сцене будет выше яблок
                                 * */
    apples.append(apple);       // Добавляем Яблоко в Список
}

Программный код урока

В данном уроке вносятся изменения в программный код предыдущих из предыдущих уроков.

После того, как описан процесс взаимодействия, привожу полный программный код класса, отвечающего за создание яблок.

apple.h

В данном файле добавлена лишь переменная, которая будет отвечать за

#ifndef APPLE_H
#define APPLE_H

#include <QObject>
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QPainter>

class Apple : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit Apple(QObject *parent = 0);
    ~Apple();

signals:

public slots:

protected:
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

private:
    int color;
};

#endif // APPLE_H

apple.cpp

#include "apple.h"

Apple::Apple(QObject *parent)
    : QObject(parent), QGraphicsItem()
{
    /* Устанавливаем улчай номер цвета яблока
     * */
    color = qrand() % ((3 + 1) - 1) + 1;
}

Apple::~Apple()
{

}

QRectF Apple::boundingRect() const
{
    return QRectF(-20,-20,40,40);   // Ограничиваем область, в которой лежит яблоко
}

void Apple::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    // Тушка яблока
    QPainterPath path(QPointF(0,-10));
    path.cubicTo(0,-10, -5,-14,  -12,-8);
    path.cubicTo(-12,-8, -20,12, -10,15);
    path.cubicTo(-10,15, -5,20, 0,16);
    path.cubicTo(0,16, 5,20, 10,15);
    path.cubicTo(10,15, 20,12, 12,-8);
    path.cubicTo(12,-8, 5,-14,  0,-10);

    /* Выбираем цвет яблока по случайно сгенерированному номеру
     * */
    switch (color)
    {
    case 1:
        painter->setBrush(Qt::red);
        break;
    case 2:
        painter->setBrush(Qt::green);
        break;
    case 3:
        painter->setBrush(Qt::yellow);
        break;
    }
    painter->drawPath(path);

    // Хвостик яблока
    painter->setPen(QPen(Qt::black, 2));
    QPainterPath path2(QPointF(0,-10));
    path2.cubicTo(0,-10,4,-18,10,-20);
    painter->setBrush(Qt::NoBrush);
    painter->drawPath(path2);

    //Листик яблока
    painter->setPen(QPen(Qt::black, 1));
    QPainterPath path3(QPointF(0,-10));
    path3.cubicTo(0,-10,-2,-20,-15,-20);
    path3.cubicTo(-15,-20,-14,-12,0,-10);
    painter->setBrush(Qt::green);
    painter->drawPath(path3);

    Q_UNUSED(option);
    Q_UNUSED(widget);
}

triangle.h

Также необходимо добавить сигнал в заголовочный файл Мухи, который будет передавать в ядро игры объекты, на которые натолкнулась Муха.

signals:
    /* Сигнал, который передаётся в ядро игры с элементом QGraphicsItem,
     * на который наткнулась муха, и требуется принять решение о том,
     * что с этим элементом делать.
     * */
    void signalCheckItem(QGraphicsItem *item);

triangle.cpp

В данном классе изменения вносятся лишь в функцию-слот, которая отвечает за обработку сигнала от игрового таймера.

void Triangle::slotGameTimer()
{
    /* Проверяем, нажата ли была какая-либо из кнопок управления объектом.
     * Прежде чем считать шажки
     * */
    /* Программный код из прошлого урока */

    /* Производим проверку на то, наткнулась ли муха на какой-нибудь
     * элемент на графической сцене.
     * Для этого определяем небольшую область перед мухой,
     * в которой будем искать элементы
     * */
    QList<QGraphicsItem *> foundItems = scene()->items(QPolygonF()
                                                           << mapToScene(0, 0)
                                                           << mapToScene(-20, -20)
                                                           << mapToScene(20, -20));
    /* После чего проверяем все элементы.
     * Один из них будет сама Муха - с ней ничего не делаем.
     * А с остальными высылаем сигнал в ядро игры
     * */
    foreach (QGraphicsItem *item, foundItems) {
        if (item == this)
            continue;
        emit signalCheckItem(item);
    }

    /* Проверка выхода за границы поля
     * Если объект выходит за заданные границы, то возвращаем его назад
     * */
    /* Программный код из прошлого урока */
}

widget.h

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

private:
    QTimer          *timerCreateApple;  // Таймер для периодического создания яблок в игре

    QList<QGraphicsItem *> apples;  // Список со всеми яблоками, присутствующими в игре
    double count;   // Переменная, которая хранит счёт игре

private slots:
    // Слот для удаления яблок если Муха наткнулая на это яблоко
    void slotDeleteApple(QGraphicsItem * item);
    void slotCreateApple();     // Слот для создания яблок, срабатывает по таймеру

widget.cpp

Реализуем функции-слоты, а также настраиваем коннекты для работы с объектами в игре.

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->resize(600,640);          /// Задаем размеры виджета, то есть окна
    this->setFixedSize(600,640);    /// Фиксируем размеры виджета

    count = 0;

    scene = new QGraphicsScene();   /// Инициализируем графическую сцену
    triangle = new Triangle();      /// Инициализируем треугольник

    ui->graphicsView->setScene(scene);  /// Устанавливаем графическую сцену в graphicsView
    ui->graphicsView->setRenderHint(QPainter::Antialiasing);    /// Устанавливаем сглаживание
    ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); /// Отключаем скроллбар по вертикали
    ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); /// Отключаем скроллбар по горизонтали

    scene->setSceneRect(-250,-250,500,500); /// Устанавливаем область графической сцены

    scene->addItem(triangle);   /// Добавляем на сцену треугольник
    triangle->setPos(0,0);      /// Устанавливаем треугольник в центр сцены



    /* Инициализируем таймер и вызываем слот обработки сигнала таймера
     * у Треугольника 100 раз в секунду.
     * Управляя скоростью отсчётов, соответственно управляем скоростью
     * изменения состояния графической сцены
     * */
    timer = new QTimer();
    connect(timer, &QTimer::timeout, triangle, &Triangle::slotGameTimer);
    timer->start(1000 / 100);

    /* Раз в секунду отсылаем сигнал на создание яблока в игре
     * */
    timerCreateApple = new QTimer();
    connect(timerCreateApple, &QTimer::timeout, this, &Widget::slotCreateApple);
    timerCreateApple->start(1000);

    /* Подключаем сигнал от Мухи, в котором передаются Объекты, на которые
     * наткнулась Муха
     * */
    connect(triangle, &Triangle::signalCheckItem, this, &Widget::slotDeleteApple);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::slotDeleteApple(QGraphicsItem *item)
{
    /* Получив сигнал от Мухи
     * Перебираем весь список яблок и удаляем найденное яблоко
     * */
    foreach (QGraphicsItem *apple, apples) {
        if(apple == item){
            scene->removeItem(apple);   // Удаляем со сцены
            apples.removeOne(item);     // Удаляем из списка
            delete apple;               // Вообще удаляем
            ui->lcdNumber->display(count++);    /* Увеличиваем счёт на единицу
                                                 * и отображаем на дисплее
                                                 * */
        }
    }
}

void Widget::slotCreateApple()
{
    Apple *apple = new Apple(); // Создаём яблоко
    scene->addItem(apple);      // Помещаем его в сцену со случайной позицией
    apple->setPos((qrand() % (251)) * ((qrand()%2 == 1)?1:-1),
                  (qrand() % (251)) * ((qrand()%2 == 1)?1:-1));
    apple->setZValue(-1);       /* Помещаем яблоко ниже Мухи, то есть Муха
                                 * на сцене будет выше яблок
                                 * */
    apples.append(apple);       // Добавляем Муху в Список
}

Итог

В результате у Вас на графической сцене будут случайно возникать яблоки, которые будет поедать Муха, а также количество съеденных яблок будет учитываться в счётчике.

Также в видеоуроке по данной статье даны дополнительные комментарии и продемонстрирована работа игры.

Полный список статей данного цикла:

Видеоурок

 

@EVILEG 20 сентября 2015 г. 22:08

Реклама

Реклама

Комментарии

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь

Реклама

Реклама