Continuing the theme of how to write a play on Qt. Once in past articles a fly was created, that eating apples, it is time to create her enemy. And the enemy flies, as is well known, the spider. Creation of game characters, who will participate in the immediate life of your main character - is not only rendering the animation action and movement, as well as the reactions of the logic on the impact of the player, but also artificial intelligence, in accordance with the logic of which will be determined by the behavior of the game character. Thus, we add to the game a new meaning, not only to eat as many apples, but to survive at any cost.
We define the behavior of the spider in this game. What should he do? Yes, the most common of all the action - to hunt a fly just chasing it on the playing field.
Also add to the game button to start the game process, and pause, and the most important thing to add - it's Game Over.
The enemy flies in the project structure
As is the case with the fly in the structure of the project added an additional class, which will be responsible for the object, which is a spider.
- spider.h - header file of spider
- spider.cpp - source file of spider
spider.h
The difference between this file from the file header flies is that it declared the game timer, which is responsible for the behavior of the Spider, the enemy of Flies is clocked by its own internal clock. Also during initialization spider lays its purpose in him, that is, fly, for which he should be relentless. In this case, the artificial intelligence is primitive to ugliness, but more at the moment and is not required.
#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(); // Init of pause signals: void signalCheckGameOver(); // Game Over signal public slots: protected: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); private: qreal angle; // The angle of rotation of the graphic object int steps; // Number position spider legs int countForSteps; // Counter to change the position of the legs QTimer *timer; // Internal timer of spider QGraphicsItem * target; // The purpose of the spider, this object is equal Flies object private slots: void slotGameTimer(); // Slot of game timer }; #endif // SPIDER_H
spider.cpp
The source code of Spider is similar to source code of Flies, with the difference that in the player slots Spider follow of Flies on the board, and spider turns to the side and flies crawling on her. And as soon as the enemy flies stumbles on some of the objects on the graphic scene, then checks whether the object Mucha, if so, what procedure is initialized 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); // Set the angle of rotation of the graphic object this->target = target; // We set the goal of spider 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){ // The first position of the legs // 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){ // The second position of the legs // 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){ // The third position of the legs // 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)); // The left mandibles 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); // The right mandibles 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); // head painter->setBrush(QColor(146, 115, 40, 255)); painter->drawEllipse(-10,-25,20,15); // Body painter->drawEllipse(-15, -15, 30, 30); // Back painter->drawEllipse(-20, 0, 40,50); painter->setPen(QPen(Qt::white,3)); painter->drawLine(-10,25,10,25); painter->drawLine(0,35,0,15); // Left eye painter->setPen(QPen(Qt::black,1)); painter->setBrush(Qt::red); painter->drawEllipse(-8,-23,6,8); // Right eye painter->setBrush(Qt::red); painter->drawEllipse(2,-23,6,8); Q_UNUSED(option); Q_UNUSED(widget); } void Spider::slotGameTimer() { // Determine the distance to Fly QLineF lineToTarget(QPointF(0, 0), mapFromItem(target, 0, 0)); // The angle of rotation in the direction of Fly qreal angleToTarget = ::acos(lineToTarget.dx() / lineToTarget.length()); if (lineToTarget.dy() < 0) angleToTarget = TwoPi - angleToTarget; angleToTarget = normalizeAngle((Pi - angleToTarget) + Pi / 2); /* Depending on whether the left or the right is from the Fly spider, * set the direction of rotation of the spider in the timer tick. * The speed of rotation depends on the angle at which you must turn the spider * */ 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); // Turned // Бежим в сторону мухи if(lineToTarget.length() >= 40){ setPos(mapToParent(0, -(qrand() % ((4 + 1) - 1) + 1))); 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; } } /* Checks for whether the spider came on any element in the graphic scene. * */ QList<QGraphicsItem *> foundItems = scene()->items(QPolygonF() << mapToScene(0, 0) << mapToScene(-2, -2) << mapToScene(2, -2)); /* Check elements. Try to find fly * */ foreach (QGraphicsItem *item, foundItems) { if (item == this) continue; if(item == target){ emit signalCheckGameOver(); } } /* Check the output of the field boundary * If the subject is beyond the set boundaries, then return it back * */ if(this->x() - 10 < -250){ this->setX(-240); // left } if(this->x() + 10 > 250){ this->setX(240); // right } if(this->y() - 10 < -250){ this->setY(-240); // top } if(this->y() + 10 > 250){ this->setY(240); // bottom } } /* Pause function, is responsible for enabling and disabling the pause * */ void Spider::pause() { if(timer->isActive()){ timer->stop(); } else { timer->start(15); } }
widget.h
In the header file, the application kernel to determine the object, which is responsible for the Spider, the variable of the game state, Pause Hot Key. And a slot for the processing start of the game, pause and Game Over procedures.
#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; // Game Timer QTimer *timerCreateApple; /// Timer for periodic QList<QGraphicsItem *> apples; /// List with all apples double count; /// Scores Spider *spider; QShortcut *pauseKey; // Pause hot key int gameState; private slots: /// The slot for the removal of apples if fly stumbled upon this apple void slotDeleteApple(QGraphicsItem * item); void slotCreateApple(); /// The slot for the creation of apples, triggered by a timer void on_pushButton_clicked(); // The slot for the start of the game void slotGameOver(); // Slot of Game Over void slotPause(); // Slot of Pause }; #endif // WIDGET_H
widget.cpp
Compared with the program code that has been in previous lessons, with the file in this tutorial, you need to thoroughly work. As the game starts by pressing pushButton button, and the program code object initialization and launch timers need to make in the slot, which is transmitted from the signal button. Also, there is a need to implement a pause function in the game, in which all the game timer is stopped and can not move objects or perform other actions. And in the slot, which is responsible for the Game Over is necessary to properly handle the deletion of all objects with the gaming scene.
#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); 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); // Init Spider scene->addItem(spider); // Add Spider to scene spider->setPos(180,180); // Set Spider position connect(spider, &Spider::signalCheckGameOver, this, &Widget::slotGameOver); 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() { /* If Game Over, then disable timers * */ 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; } 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(); } } }
Result
According to the results of the work ceases to be a game of infinite value of scores will increase at times.
A full list of articles in this series:
- Lesson 1. How to make game using Qt. Control of object
- Lesson 2. How to make game using Qt. Animation game hero (2D)
- Lesson 3. How to make game using Qt. Interaction with other objects
- Lesson 4. How to make game using Qt. Enemy – meaning in the survival
- Lesson 5. How to make game using Qt. Adding sound QMediaPlayer
Здравствуй у меня опять проблема как решить? 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 ^ ^
вставляю кнопку вообще 10 ошибок не определена ссылка на паука и тд
Добавьте через графический дизайнер кнопку в widget.ui и проследите, чтобы название кнопки было pushButton
добавил кнопку все равно много ошибок вот скрины http://priscree.ru/img/e8e0d04f9e232c.jpg http://priscree.ru/img/3fb040824e8665.jpg
ошибки в vtable... Это moc файлы попортились. Нужно удалить build сборку и пересобрать, и если не поможет, то создать новый проект и переписать в него код. Иначе никак.
хорошо попробую переписать все.как напишу отпишусь)
все заработало спасибо еще вопрос у нас препод просит чтобы мы сделали так.чтобы можно было сохранять результаты это возможно ?
Конечно, возможно. Можно сохранять в файл. Можно сохранять в базу данных. Можно даже в реестр писать с помощью QSettings . Мне больше всего нравится база данных для таких целей. По окончании игры можно добавлять результат в базу данных. Также можно добавить кнопку, которая бы открывала окно с результатами.
а как это сделать ? можете помочь если не сложно конечно и есть время
Значит так. Просто писать код за вас я не буду, но подсказать и направить на нужный путь - это без проблем.
Поэтому -> В предыдущем сообщении я дал ссылку на статью с базой данных. Изучите её. Изучите, как добавлять записи в базу данных. Там же показано, как отобразить записи в таблице. А потом попытайтесь по окончании игры сделать добавление строк в базу данных. В коде есть место, где по окончании игры вызывается диалог. Это происходит в слоте slotGameOver . Вот в нём можно сделать добавление записей в базу данных. Также можете добавить кнопку в окно игры, по нажатию которой можно будет вызвать диалог, в котором как раз и будете показывать рекорды. Если есть затруднения с пониманием сигналов и слотов, то изучите следующую статью .
Здравствуйте,
Подскажите, пжлст, как работает этот код :
Самое непонятное - это 5 -ая строчка, зачем добавлять 90 градусов?
не 90 на 45, ошибся
Там неправильный подсчёт был по направлению и полный бардак был с поведением, эти 45 градусов исправляли ситуацию. Точную причину уже совсем не помню.
А вообще все эти расчёты довольно тяжелы для процессора, я подобрал более лёгкий вариант со скалярными векторами. Но статью ещё пока не недописал к сожалению (она пока в разработке).
Здравствуйте! Почему может быть такая ошибка: враг висит в углу, колеблется в нём, но не двигается? По логике код такой же, но вместо орисовки моделей использую спрайты. Со спрайтом "мухи" всё хорошо, а вот "паук" отказывается вести себя нормально. Подозреваю, что ошибка может быть здесь (это код, который идёт вместо отрисовки):
Или же ошибка где-то в управлении "паука", но там код вообще неизменный. (в Spider.cpp, void Spider...)
Добрый день.
Дело тут явно не в методе paint, скорее всего всё-таки есть какая-то ошибка в другом месте. Обычно такое может быть в районе метода slotGamerTimer
Спасибо! На самом деле ошибка оказалась от невнимательности - просто пропустила подключение движения в сторону модели. Теперь всё работает.
Если вам не сложно, могли бы вы, пожалуйста, объяснить, за что отвечают параметры в скобках тут: 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!
П.С.: я не очень умею пока читать документацию( поэтому прошу помощи у вас, чтобы разобраться с этими параметрами