Evgenii Legotckoi
Evgenii Legotckoi22 сентября 2015 г. 12:36

Как написать игру на Qt - Урок 4. Враг - смысл в выживании

Продолжаем тему того, как написать игру на Qt. После того, как в прошлых статьях была создана Муха, которая поедает яблоки, настало время создать ей врага. А Враг Мухи , как известно, Паук . Создание игровых персонажей, которые будут участвовать в непосредственной жизни Вашего главного героя - это не только отрисовка анимации действий и передвижения, а также логика реакций на воздействия игрока, но и искусственный интеллект, в соответствии с логикой которого будет определятся поведение игрового персонажа. Таким образом, мы добавляем в игру новый смысл, не только съесть как можно больше яблок, но и выжить любой ценой.

Определим поведение Паука в данной версии игры. Что же он должен делать? Да самое обычное из всех действий - охотиться на Муху , просто гоняться за ней по игровому полю.

Также добавим в игру кнопку для запуска игрового процесса, и паузу, и самое главное, что добавим - это Game Over.

Враг мухи в структуре проекта

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

  • spider.h - заголовочный файл класса, отвечающего за Паука
  • spider.cpp - файл исходных кодов, отвечающий за Паука

spider.h

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

#ifndef SPIDER_H
#define SPIDER_H

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

class Spider : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit Spider(QGraphicsItem * target, QObject *parent = 0);
    ~Spider();
    void pause();   // Сигнал для инициализации паузы

signals:
    void signalCheckGameOver();  // Сигнал на вызов состояния Game Over

public slots:

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

private:
    qreal angle;        // Угол поворота графического объекта
    int steps;          // Номер положения ножек паука
    int countForSteps;  // Счетчик для изменения полоэения ножек
    QTimer      *timer;     // Внутренний таймер паука, по которому инициализируется его движение
    QGraphicsItem * target; // Цель паука, данный объект приравнивается объекту Мухи

private slots:
    void slotGameTimer();   // Слот игрового таймера паука
};

#endif // SPIDER_H

spider.cpp

Исходный код Паука аналогичен исходному коду Мухи , с тем отличием, что в игровом Слоте Паука отслеживается положение Мухи на игровом поле, и Паук разворачивается в сторону Мухи и ползёт за ней. А как только враг мухи натыкается на какой-нибудь из объектов на графической сцене, то он производит проверку, является ли этот объект Мухой , если да, то инициализируется процедура Game Over .

#include "spider.h"
#include "math.h"

static const double Pi = 3.14159265358979323846264338327950288419717;
static double TwoPi = 2.0 * Pi;

static qreal normalizeAngle(qreal angle)
{
    while (angle < 0)
        angle += TwoPi;
    while (angle > TwoPi)
        angle -= TwoPi;
    return angle;
}

Spider::Spider(QGraphicsItem *target, QObject *parent) :
    QObject(parent), QGraphicsItem()
{
    angle = 0;      // Задаём угол поворота графического объекта
    steps = 0;      // Задаём стартовое положение ножек мухи
    countForSteps = 0;      // Счётчик для отсчета тиков таймера, при которых мы нажимали на кнопки
    setRotation(angle);     // Устанавливаем угол поворота графического объекта

    this->target = target;  // Устанавливаем цель паука

    timer = new QTimer();   // Инициализируем игровой таймер паука
    // подключаем сигнал таймера к игровому слоту паука
    connect(timer, &QTimer::timeout, this, &Spider::slotGameTimer);
    timer->start(15);       // Запускаем таймер
}

Spider::~Spider()
{

}

QRectF Spider::boundingRect() const
{
    return QRectF(-40,-50,80,100);
}

void Spider::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    // Рисуем ножки, без ножек паук не догонит муху
    painter->setPen(QPen(Qt::black, 2));
    if(steps == 0){     // Первое положение ножек
        // Left 1
        painter->drawLine(-24,-45,-28,-35);
        painter->drawLine(-28,-35,-22,-10);
        painter->drawLine(-22,-10,0,0);
        // Right 1
        painter->drawLine(24,-45,28,-35);
        painter->drawLine(28,-35,22,-10);
        painter->drawLine(22,-10,0,0);

        // Left 2
        painter->drawLine(-35,-38,-30,-18);
        painter->drawLine(-30,-18,-25,-3);
        painter->drawLine(-25,-3,0,0);
        // Right 2
        painter->drawLine(35,-38,30,-18);
        painter->drawLine(30,-18,25,-3);
        painter->drawLine(25,-3,0,0);

        // Left 3
        painter->drawLine(-35,38,-30,18);
        painter->drawLine(-30,18,-25,3);
        painter->drawLine(-25,3,0,0);
        // Right 3
        painter->drawLine(35,38,30,18);
        painter->drawLine(30,18,25,3);
        painter->drawLine(25,3,0,0);

        // Left 4
        painter->drawLine(-24,45,-28,35);
        painter->drawLine(-28,35,-22,10);
        painter->drawLine(-22,10,0,0);
        // Right 4
        painter->drawLine(24,45,28,35);
        painter->drawLine(28,35,22,10);
        painter->drawLine(22,10,0,0);
    } else if (steps == 1){     // Второе положение ножек
        // Left 1
        painter->drawLine(-23,-40,-24,-30);
        painter->drawLine(-24,-30,-19,-9);
        painter->drawLine(-19,-9,0,0);
        // Right 1
        painter->drawLine(20,-50,23,-40);
        painter->drawLine(23,-40,15,-12);
        painter->drawLine(15,-12,0,0);

        // Left 2
        painter->drawLine(-30,-35,-27,-24);
        painter->drawLine(-27,-24,-23,-5);
        painter->drawLine(-23,-5,0,0);
        // Right 2
        painter->drawLine(40,-27,35,-10);
        painter->drawLine(35,-10,28,-1);
        painter->drawLine(28,-1,0,0);

        // Left 3
        painter->drawLine(-40,27,-35,10);
        painter->drawLine(-35,10,-28,1);
        painter->drawLine(-28,1,0,0);
        // Right 3
        painter->drawLine(30,35,27,24);
        painter->drawLine(27,24,23,5);
        painter->drawLine(23,5,0,0);

        // Left 4
        painter->drawLine(-20,50,-27,30);
        painter->drawLine(-27,30,-20,12);
        painter->drawLine(-20,12,0,0);
        // Right 4
        painter->drawLine(23,40,24,30);
        painter->drawLine(24,30,19,9);
        painter->drawLine(19,9,0,0);
    } else if (steps == 2){     // Третье положение ножек
        // Left 1
        painter->drawLine(-20,-50,-23,-40);
        painter->drawLine(-23,-40,-15,-12);
        painter->drawLine(-15,-12,0,0);
        // Right 1
        painter->drawLine(23,-40,24,-30);
        painter->drawLine(24,-30,19,-9);
        painter->drawLine(19,-9,0,0);

        // Left 2
        painter->drawLine(-40,-27,-35,-10);
        painter->drawLine(-35,-10,-28,-1);
        painter->drawLine(-28,-1,0,0);
        // Right 2
        painter->drawLine(30,-35,27,-24);
        painter->drawLine(27,-24,23,-5);
        painter->drawLine(23,-5,0,0);

        // Left 3
        painter->drawLine(-30,35,-27,24);
        painter->drawLine(-27,24,-23,5);
        painter->drawLine(-23,5,0,0);
        // Right 3
        painter->drawLine(40,27,35,10);
        painter->drawLine(35,10,28,1);
        painter->drawLine(28,1,0,0);

        // Left 4
        painter->drawLine(-23,40,-24,30);
        painter->drawLine(-24,30,-19,9);
        painter->drawLine(-19,9,0,0);
        // Right 4
        painter->drawLine(20,50,27,30);
        painter->drawLine(27,30,20,12);
        painter->drawLine(20,12,0,0);
    }

    painter->setPen(QPen(Qt::black, 1));
    // Левое Жвало
    QPainterPath path1(QPointF(0, -20));
    path1.cubicTo(0, -20, -5, -25, -3, -35);
    path1.cubicTo(-3,-35,-15,-25,-8,-17);
    path1.cubicTo(-8,-17,-5,15,0,-20 );
    painter->setBrush(Qt::black);
    painter->drawPath(path1);

    // Правое Жвало
    QPainterPath path2(QPointF(0, -20));
    path2.cubicTo(0, -20, 5, -25, 3, -35);
    path2.cubicTo(3,-35,15,-25,8,-17);
    path2.cubicTo(8,-17,5,15,0,-20 );
    painter->setBrush(Qt::black);
    painter->drawPath(path2);

    // Голова
    painter->setBrush(QColor(146, 115, 40, 255));
    painter->drawEllipse(-10,-25,20,15);
    // Тушка
    painter->drawEllipse(-15, -15, 30, 30);
    // Жопка
    painter->drawEllipse(-20, 0, 40,50);
    painter->setPen(QPen(Qt::white,3));
    painter->drawLine(-10,25,10,25);
    painter->drawLine(0,35,0,15);
    // Левое глазище
    painter->setPen(QPen(Qt::black,1));
    painter->setBrush(Qt::red);
    painter->drawEllipse(-8,-23,6,8);
    // Правое глазище
    painter->setBrush(Qt::red);
    painter->drawEllipse(2,-23,6,8);

    Q_UNUSED(option);
    Q_UNUSED(widget);
}

void Spider::slotGameTimer()
{
    // Определяем расстояние до Мухи
    QLineF lineToTarget(QPointF(0, 0), mapFromItem(target, 0, 0));
    // Угол поворота в направлении к Мухе
    qreal angleToTarget = ::acos(lineToTarget.dx() / lineToTarget.length());
    if (lineToTarget.dy() < 0)
        angleToTarget = TwoPi - angleToTarget;
    angleToTarget = normalizeAngle((Pi - angleToTarget) + Pi / 2);

    /* В Зависимости от того, слева или справа находится Муха от Паука,
     * устанавливаем направление поворота паука в данном тике таймера
     * Скорость разворота зависит от угла, на который необходимо повернуться пауку
     * */
    if (angleToTarget > 0 && angleToTarget < Pi) {
        // Rotate left
        if(angleToTarget > Pi / 5){
            angle = -15;
        } else if(angleToTarget > Pi / 10){
            angle = -5;
        } else {
            angle = -1;
        }
    } else if (angleToTarget <= TwoPi && angleToTarget > (TwoPi - Pi)) {
        // Rotate right
        if(angleToTarget < (TwoPi - Pi / 5)){
            angle = 15;
        } else if(angleToTarget < (TwoPi - Pi / 10)){
            angle = 5;
        } else {
            angle = 1;
        }
    } else if(angleToTarget == 0) {
        angle = 0;
    }

    setRotation(rotation() + angle); // Разворачиваемся

    // Бежим в сторону мухи
    if(lineToTarget.length() >= 40){
        setPos(mapToParent(0, -(qrand() % ((4 + 1) - 1) + 1)));

        // Двигаем ножками, Dance, dance, Spidy !!!
        countForSteps++;
        if(countForSteps == 6){
            steps = 1;
            update(QRectF(-40,-50,80,100));
        } else if (countForSteps == 12){
            steps = 0;
            update(QRectF(-40,-50,80,100));
        } else if (countForSteps == 18){
            steps = 2;
            update(QRectF(-40,-50,80,100));
        } else if (countForSteps == 24) {
            steps = 0;
            update(QRectF(-40,-50,80,100));
            countForSteps = 0;
        }
    }

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

    /* Проверка выхода за границы поля
     * Если объект выходит за заданные границы, то возвращаем его назад
     * */
    if(this->x() - 10 < -250){
        this->setX(-240);       // слева
    }
    if(this->x() + 10 > 250){
        this->setX(240);        // справа
    }

    if(this->y() - 10 < -250){
        this->setY(-240);       // сверху
    }
    if(this->y() + 10 > 250){
        this->setY(240);        // снизу
    }
}

/* Функция паузы, отвечает за включение и отключение паузы
 * */
void Spider::pause()
{
    if(timer->isActive()){
        timer->stop();
    } else {
        timer->start(15);
    }
}

widget.h

В заголовочном файле ядра приложения необходимо определить объект, который отвечает за Паука, переменную состояния игры и Горячую клавишу паузы. А также слоты для обработки запуска игры, паузы и процедуры Game Over .

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QShortcut>
#include <QDebug>
#include <QTimer>
#include <QMessageBox>

#include <triangle.h>
#include <apple.h>
#include <spider.h>

#define GAME_STOPED 0
#define GAME_STARTED 1

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget      *ui;
    QGraphicsScene  *scene;     /// Объявляем графическую сцену
    Triangle        *triangle;  /// и треугольник
    QTimer          *timer;     /** Объявляем игровой таймер, благодаря которому
                                 * будет производиться изменения положения объекта на сцене
                                 * При воздействии на него клавишами клавиатуры
                                 * */
    QTimer          *timerCreateApple;  /// Таймер для периодического создания яблок в игре

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

    Spider          *spider;        // Объект Паука

    QShortcut       *pauseKey;      // Горячая клавиша, отвечающая за паузу в игре
    int             gameState;      /* Переменная, которая хранит состояние игры.
                                     * То есть, если игра запущена, то статус будет GAME_STARTED,
                                     * в противном случае GAME_STOPED
                                     * */

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

    void on_pushButton_clicked();               // Слот для запуска игры
    void slotGameOver();                        // Слот инициализации Game Over
    void slotPause();                           // Слот для обработки паузы
};

#endif // WIDGET_H

widget.cpp

По сравнению с программным кодом, который был в предыдущих уроках, с данным файлом в этом уроке необходимо основательно поработать. Поскольку игра, стартует по нажатию кнопки pushButton, то и программный код инициализации объектов и запуска таймеров необходимо вынести в слот, которому передаётся сигнал от кнопки. Также возникает необходимость реализовать функцию паузы в игре, по которой все игровые таймеры останавливаются и объекты не могут передвигаться или производить иных действий. А в слоте, который отвечает за Game Over необходимо правильно обработать удаление всех объектов с игровой сцены.

#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);    /// Фиксируем размеры виджета

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

    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); /// Устанавливаем область графической сцены

    timer = new QTimer();
    timerCreateApple = new QTimer();

    gameState = GAME_STOPED;

    pauseKey = new QShortcut(this);
    pauseKey->setKey(Qt::Key_Pause);
    connect(pauseKey, &QShortcut::activated, this, &Widget::slotPause);


}

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

void Widget::slotDeleteApple(QGraphicsItem *item)
{
    /* Получив сигнал от Мухи
     * Перебираем весь список яблок и удаляем найденное яблоко
     * */
    foreach (QGraphicsItem *apple, apples) {
        if(apple == item){
            scene->removeItem(apple);   // Удаляем со сцены
            apples.removeOne(apple);     // Удаляем из списка
            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);       // Добавляем Муху в Список
}

void Widget::on_pushButton_clicked()
{
    count = 0;
    ui->lcdNumber->display(count);
    triangle = new Triangle();      /// Инициализируем муху

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

    spider = new Spider(triangle);  // Инициализируем паука
    scene->addItem(spider);         // Добавляем паука на сцену
    spider->setPos(180,180);        // Устанавливаем позицию паука

    /* Подключаем сигнал от паука на проверку состояния GameOver
     * */
    connect(spider, &Spider::signalCheckGameOver, this, &Widget::slotGameOver);

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

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

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

    ui->pushButton->setEnabled(false);

    gameState = GAME_STARTED;
}

void Widget::slotGameOver()
{
    /* Если игра окончена
     * Отключаем все таймеры
     * */
    timer->stop();
    timerCreateApple->stop();
    QMessageBox::warning(this,
                         "Game Over",
                         "Мои соболезнования, но Вас только что слопали");
    /* Отключаем все сигналы от слотов
     * */
    disconnect(timerCreateApple, &QTimer::timeout, this, &Widget::slotCreateApple);
    disconnect(triangle, &Triangle::signalCheckItem, this, &Widget::slotDeleteApple);
    disconnect(spider, &Spider::signalCheckGameOver, this, &Widget::slotGameOver);
    /* И удаляем все объекты со сцены
     * */
    spider->deleteLater();
    triangle->deleteLater();

    foreach (QGraphicsItem *apple, apples) {
        scene->removeItem(apple);   // Удаляем со сцены
        apples.removeOne(apple);    // Удаляем из списка
        delete apple;               // Вообще удаляем
    }
    /* Активируем кнопку старта игры
     * */
    ui->pushButton->setEnabled(true);

    gameState = GAME_STOPED; // Устанавливаем состояние игры в GAME_STOPED
}

void Widget::slotPause()
{
    /* Выполняем проверку на состояние игры,
     * если игра не запущена, то игнорируем Паузу.
     * В противном случае или запускаем, или останавливаем все таймеры
     * */
    if(gameState == GAME_STARTED){
        if(timer->isActive()){
            timer->stop();
            timerCreateApple->stop();
            spider->pause();
        } else {
            timer->start(1000/100);
            timerCreateApple->start(1000);
            spider->pause();
        }
    }
}

Итог

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

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

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

Видеоурок

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

Вам это нравится? Поделитесь в социальных сетях!

v
  • 9 мая 2017 г. 9:27

Здравствуй у меня опять проблема как решить? In member function 'void Widget::on_pushButton_clicked()': ошибка: 'class Ui::Widget' has no member named 'pushButton' ui->pushButton->setEnabled(false); : In member function 'void Widget::slotGameOver()': ошибка: 'class Ui::Widget' has no member named 'pushButton' ui->pushButton->setEnabled(true); скрин http://priscree.ru/img/a9de07f8d2a5a0.jpg ^ ^

v
  • 9 мая 2017 г. 9:46

вставляю кнопку вообще 10 ошибок не определена ссылка на паука и тд

Evgenii Legotckoi
  • 10 мая 2017 г. 2:06

Добавьте через графический дизайнер кнопку в widget.ui и проследите, чтобы название кнопки было pushButton

v
  • 10 мая 2017 г. 4:40

добавил кнопку все равно много ошибок вот скрины http://priscree.ru/img/e8e0d04f9e232c.jpg http://priscree.ru/img/3fb040824e8665.jpg

Evgenii Legotckoi
  • 10 мая 2017 г. 5:21

ошибки в vtable... Это moc файлы попортились. Нужно удалить build сборку и пересобрать, и если не поможет, то создать новый проект и переписать в него код. Иначе никак.

v
  • 10 мая 2017 г. 7:00

хорошо попробую переписать все.как напишу отпишусь)

v
  • 10 мая 2017 г. 8:58

все заработало спасибо еще вопрос у нас препод просит чтобы мы сделали так.чтобы можно было сохранять результаты это возможно ?

Evgenii Legotckoi
  • 10 мая 2017 г. 9:20

Конечно, возможно. Можно сохранять в файл. Можно сохранять в базу данных. Можно даже в реестр писать с помощью QSettings . Мне больше всего нравится база данных для таких целей. По окончании игры можно добавлять результат в базу данных. Также можно добавить кнопку, которая бы открывала окно с результатами.

v
  • 10 мая 2017 г. 9:46

а как это сделать ? можете помочь если не сложно конечно и есть время

Evgenii Legotckoi
  • 10 мая 2017 г. 10:46

Значит так. Просто писать код за вас я не буду, но подсказать и направить на нужный путь - это без проблем.

Поэтому -> В предыдущем сообщении я дал ссылку на статью с базой данных. Изучите её. Изучите, как добавлять записи в базу данных. Там же показано, как отобразить записи в таблице. А потом попытайтесь по окончании игры сделать добавление строк в базу данных. В коде есть место, где по окончании игры вызывается диалог. Это происходит в слоте slotGameOver . Вот в нём можно сделать добавление записей в базу данных. Также можете добавить кнопку в окно игры, по нажатию которой можно будет вызвать диалог, в котором как раз и будете показывать рекорды. Если есть затруднения с пониманием сигналов и слотов, то изучите следующую статью .

M
  • 25 июня 2018 г. 5:13

Здравствуйте,
Подскажите, пжлст, как работает этот код :

  1. QLineF lineToTarget(QPointF(0, 0), mapFromItem(target, 0, 0));  // Проводим линию от паука к мухе
  2. qreal angleToTarget = ::acos(lineToTarget.dx() / lineToTarget.length()); // Находим угол. Например 120 гр( 2пи/3 )
  3. if (lineToTarget.dy() < 0)                                             //Если y отрицательный зачем отнимать?
  4. angleToTarget = TwoPi - angleToTarget;                 // ??
  5. angleToTarget = normalizeAngle((Pi - angleToTarget) + Pi / 2); // Тут мы убираем намотку по 2пи. Но зачем добавлять 90 гр?
    Самое непонятное - это 5 -ая строчка, зачем добавлять 90 градусов?

M
  • 25 июня 2018 г. 5:25

не 90 на 45, ошибся

Evgenii Legotckoi
  • 25 июня 2018 г. 5:34

Там неправильный подсчёт был по направлению и полный бардак был с поведением, эти 45 градусов исправляли ситуацию. Точную причину уже совсем не помню.

А вообще все эти расчёты довольно тяжелы для процессора, я подобрал более лёгкий вариант со скалярными векторами. Но статью ещё пока не недописал к сожалению (она пока в разработке).

РР
  • 4 июня 2019 г. 11:28
  • (ред.)

Здравствуйте! Почему может быть такая ошибка: враг висит в углу, колеблется в нём, но не двигается? По логике код такой же, но вместо орисовки моделей использую спрайты. Со спрайтом "мухи" всё хорошо, а вот "паук" отказывается вести себя нормально. Подозреваю, что ошибка может быть здесь (это код, который идёт вместо отрисовки):

void Crocodillo::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->drawPixmap(-10,-10, *spriteImageCroco, currentFrameCroco, 0, 96,95);
    Q_UNUSED(option);
    Q_UNUSED(widget);
}

Или же ошибка где-то в управлении "паука", но там код вообще неизменный. (в Spider.cpp, void Spider...)

Evgenii Legotckoi
  • 5 июня 2019 г. 3:44

Добрый день.

Дело тут явно не в методе paint, скорее всего всё-таки есть какая-то ошибка в другом месте. Обычно такое может быть в районе метода slotGamerTimer

РР
  • 5 июня 2019 г. 20:27
  • (ред.)

Спасибо! На самом деле ошибка оказалась от невнимательности - просто пропустила подключение движения в сторону модели. Теперь всё работает.

Если вам не сложно, могли бы вы, пожалуйста, объяснить, за что отвечают параметры в скобках тут: setPos(mapToParent(0, -(qrand() % ((2 + 1) - 1) + 1)))?
Нашла только такое, не много о чём говорит: (qrand() % ((high + 1) - low) + low). Экспериментально кое-что выяснила, но хотелось бы знать точно)
А ещё вот тут, когда генерировали бананы: banana->setPos((qrand() % (251)) * ((qrand()%2 == 1)?1:-1),
(qrand() % (251)) * ((qrand()%2 == 1)?1:-1));

А так, благодарю за указку на slotGameTimer!

П.С.: я не очень умею пока читать документацию( поэтому прошу помощи у вас, чтобы разобраться с этими параметрами

Комментарии

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

C++ - Тест 006. Перечисления

  • Результат:10баллов,
  • Очки рейтинга-10
K
  • KiRi4
  • 7 сентября 2023 г. 7:57

C++ - Тест 002. Константы

  • Результат:41баллов,
  • Очки рейтинга-8
K
  • KiRi4
  • 7 сентября 2023 г. 7:49

C++ - Тест 001. Первая программа и типы данных

  • Результат:66баллов,
  • Очки рейтинга-1
Последние комментарии
IscanderChe
IscanderChe13 сентября 2023 г. 9:11
Пример использования QScintilla C++ По горячим следам (с другого форума вопрос задали, пришлось в памяти освежить всё) решил дополнить. Качаем исходники с https://riverbankcomputing.com/software/qscintilla/downlo…
Evgenii Legotckoi
Evgenii Legotckoi6 сентября 2023 г. 7:18
Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread Разве могут взаимодействовать объекты из разных нитей как-то, кроме как через сигнал-слоты?" Могут. Выполняя оператор new , Вы выделяете под объект память в куче (heap), …
AC
Andrei Cherniaev5 сентября 2023 г. 3:37
Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread Я поясню свой вопрос. Выше я писал "Почему же в методе MainWindow::on_write_1_clicked() Можно обращаться к методам exampleObject_1? Разве могут взаимодействовать объекты из разных…
n
nvn31 августа 2023 г. 9:47
QML - Урок 004. Сигналы и слоты в Qt QML Здравствуйте! Прекрасный сайт, отличные статьи. Не хватает только готовых проектов для скачивания. Многих комментариев типа appCore != AppCore просто бы не было )))
NSProject
NSProject24 августа 2023 г. 13:40
Django - Урок 023. Like Dislike система с помощью GenericForeignKey Ваша ошибка связана с gettext from django.utils.translation import gettext_lazy as _ Поле должно выглядеть так vote = models.SmallIntegerField(verbose_name=_("Голос"), choices=VOTES) …
Сейчас обсуждают на форуме
IscanderChe
IscanderChe17 сентября 2023 г. 9:24
Интернационализация строк в QMessageBox Странная картина... Сделал минимально работающий пример - всё работает. Попробую на другой операционке. Может, дело в этом.
NSProject
NSProject17 сентября 2023 г. 8:49
Помогите добавить Ajax в проект В принципе ничего сложного с отправкой на сервер нет. Всё что ты хочешь отобразить на странице передаётся в шаблон и рендерится. Ты просто создаёшь файл forms.py в нём описываешь свою форму и в …
BlinCT
BlinCT15 сентября 2023 г. 12:35
Размеры полей в TreeView Всем привет. Пытаюсь сделать дерево вот такого вида Пытаюсь организовать делегат для каждой строки в дереве. ТО есть отступ какого то размера и если при открытии есть под…
IscanderChe
IscanderChe8 сентября 2023 г. 12:07
Кастомная QAbstractListModel и цвет фона, цвет текста и шрифт Похоже надо не абстрактный , а "реальный" типа QSqlTableModel Да, но не совсем. Решилось с помощью стайлшитов и setFont. Спасибо за отлик!
Evgenii Legotckoi
Evgenii Legotckoi6 сентября 2023 г. 6:35
Вопрос: Нужно ли в деструкторе удалять динамически созданные QT-объекты. Напр: Зависит от того, как эти объекты были созданы. Если вы передаёте указатель на parent объект, то не нужно, Ядро Qt само разрулит удаление, если нет, то нужно удалять вручную, иначе будет ут…

Следите за нами в социальных сетях