Менің назарымды [QGraphicsItem және QObject] мұраға алынған сыныптың орнына сигналдармен және ұяшықтармен жұмыс істеу үшін аударды (https:// evileg.com/ ru/post/81/) сіз QGraphicsObject ішінен мұраланған классты пайдалана аласыз. Шынында да, егер сіз QGraphicsObject көздеріне аздап үңілсеңіз, бұл QGraphicsItem және QObject ішінен мұраланған класс екенін көресіз. Яғни, бірнеше мұра да қолданылады, тек осы жағдайда барлық велосипедтер бізге дейін жазылған. Сондықтан осы сыныппен ойын механикасы мысалында жұмыс істеуге тырысайық.
Атап айтқанда, мен Diablo. сияқты кез келген RPG сияқты графикалық көріністе кейіпкерді тінтуірдің көмегімен жылжытатын бағдарлама жазуды ұсынамын.
QGraphicsObject-пен жұмыс істеуге арналған жоба құрылымы
- QGraphicsObjectExample.pro - профильдік жоба;
- main.cpp - негізгі бастапқы код файлы;
- widget.h - қолданба терезесінің тақырып файлы;
- widget.cpp - қолданба терезесінің бастапқы код файлы;
- customscene.h - теңшелетін [графикалық көрініс] тақырып файлы(https://evileg.com/en/post/82/) ;
- customscene.cpp - реттелетін графикалық көріністің бастапқы код файлы;
- triangle.h - жылжитын үшбұрыш батыр класының тақырып файлы;
- triangle.cpp - үшбұрышты кейіпкер сыныбының бастапқы код файлы.
mainwindow.ui - QGraphicsObjectExample.pro - main.cpp
Негізгі терезенің пішіні ерекше. Ол тек QGraphicsView нысанын қамтиды. QGraphicsObjectExample және main.cpp файлдары әдепкі бойынша жасалады және өзгертілмейді.
виджет.h
Жоспарларымызды жүзеге асыру үшін бізге тек теңшелген графикалық көрініс қажет, басқа ештеңе жоқ. Ол Виджет класының конструкторында жергілікті түрде жариялануы мүмкін болса да.
#ifndef WIDGET\_H #define WIDGET\_H #include <QWidget> #include "customscene.h" #include "triangle.h" namespace Ui { class Widget; } class Widget : public QWidget { Q\_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private: Ui::Widget *ui; CustomScene *scene; }; #endif // WIDGET\_H
widget.cpp
Виджет класының конструкторында графикалық көрініс пен үшбұрыш нысанын инициализациялаймыз. Сондай-ақ signalTargetCoordinate сигналын графикалық көріністен үшбұрыш ұясына қосамыз. signalTargetCoordinate үшбұрыш қозғалатын тінтуірдің координаталарын береді.
#include "widget.h" #include "ui\_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { // Устанавливаем параметры окна приложения this->resize(600,600); this->setFixedSize(600,600); ui->setupUi(this); scene = new CustomScene(); ui->graphicsView->setScene(scene); ui->graphicsView->setRenderHint(QPainter::Antialiasing); // Устанавливаем сглаживание ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Отключаем скроллбар по вертикали ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Отключаем скроллбар по горизонтали scene->setSceneRect(0,0,520,520); // Устанавливаем размеры графической сцены Triangle *triangle = new Triangle(); triangle->setPos(260,260); scene->addItem(triangle); // Соединяем сигнал о положении курсора со слотом для передвижения героя connect(scene, &CustomScene::signalTargetCoordinate, triangle, &Triangle::slotTarget); } Widget::~Widget() { delete ui; }
customscene.h
Графикалық көріністе тінтуірдің координаттарын жіберу үшін сигнал пайдаланылады және ол mousePressEvent және mouseMoveEvent. оқиғаларынан жіберіледі.Бұл жағдайда деректері бар сигнал сол жақ тінтуірдің түймесі басылды.
#ifndef CUSTOMSCENE\_H #define CUSTOMSCENE\_H #include <QObject> #include <QGraphicsScene> #include <QGraphicsSceneMouseEvent> class CustomScene : public QGraphicsScene { Q\_OBJECT public: explicit CustomScene(QObject *parent = 0); ~CustomScene(); signals: // Сигнал для передачи координат положения курсора мыши void signalTargetCoordinate(QPointF point); public slots: private: // Функция, в которой производится отслеживание положения мыши void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mousePressEvent(QGraphicsSceneMouseEvent *event); }; #endif // CUSTOMSCENE\_H
customscene.cpp
#include "customscene.h" #include <QApplication> CustomScene::CustomScene(QObject *parent) : QGraphicsScene(parent) { } CustomScene::~CustomScene() { } void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if(QApplication::mouseButtons() == Qt::LeftButton){ emit signalTargetCoordinate(event->scenePos()); // Передаём данный о местоположении клика } } void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event) { if(QApplication::mouseButtons() == Qt::LeftButton){ emit signalTargetCoordinate(event->scenePos()); // Передаём данный о местоположении клика } }
үшбұрыш.сағ
Бұл класс QGraphicsObject сыныбынан мұра алады және QObject сыныбынан мұраны пайдаланбайды, өйткені QGraphicsObject одан, сондай-ақ QGrapihcsItem ішінен мұраланған. Әрі қарай жұмыс істеу. бұл нысан да орындалады, біз QGrapgicsItem. ішінен мұраланған сыныппен жұмыс істегендей орындалады.
GameDev туралы басқа мақалалармен салыстырғанда, мысалы, кейіпкердің тінтуірінің қозғалысын қадағалайтын мақаламен салыстырғанда шағын мәселе - күй айнымалысы бар. тұру / жүру.
Бұл батырды қабырғаға немесе аумақтың шекарасына соғып, оның келу нүктесіне жету мүмкін болмаған жағдайда тоқтату үшін қажет. Сонымен, біз мұнда басты кейіпкердің қозғалыс процесі өңделетін ойын ұясы бар ойын таймер қолданамыз.
Бірақ мақсатты нүктенің координаттарын алуға арналған ұя кейіпкердің нысанаға қарай айналуын өңдейді.
#ifndef TRIANGLE\_H #define TRIANGLE\_H #include <QObject> #include <QTimer> #include <QGraphicsObject> #define GO true #define STOP false class Triangle : public QGraphicsObject { Q\_OBJECT public: explicit Triangle(); signals: private: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); public slots: void slotTarget(QPointF point); // Слот для установки координаты, куда нужно идти private: QTimer *gameTimer; // Игровой таймер QPointF target; // Положение курсора bool state; // Статус идти/стоять private slots: void slotGameTimer(); // Игровой слот }; #endif // TRIANGLE\_H
triangle.cpp
#include "triangle.h" #include <math.h> #include <QPainter> 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; } Triangle::Triangle() : QGraphicsObject() { setRotation(0); state = STOP; gameTimer = new QTimer(); // Инициализируем игровой таймер // Подключаем сигнал от таймера и слоту обработки игрового таймера connect(gameTimer, &QTimer::timeout, this, &Triangle::slotGameTimer); gameTimer->start(5); // Стартуем таймер } QRectF Triangle::boundingRect() const { return QRectF(-12,-15,24,30); } void Triangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { // Отрисовка треугольника QPolygon polygon; polygon << QPoint(0,-15) << QPoint(12,15) << QPoint(-12,15); painter->setBrush(Qt::red); painter->drawPolygon(polygon); Q\_UNUSED(option); Q\_UNUSED(widget); } void Triangle::slotGameTimer() { if(state){ QLineF lineToTarget(QPoint(0,0), mapFromScene(target)); if(lineToTarget.length() > 2){ setPos(mapToParent(0, -2)); } /* Проверка выхода за границы поля * Если объект выходит за заданные границы, то возвращаем его назад * */ if(this->x() - 30 < 0){ this->setX(30); /// слева state = STOP; // Останавливаемся } if(this->x() + 30 > 520){ this->setX(520 - 30); /// справа state = STOP; // Останавливаемся } if(this->y() - 30 < 0){ this->setY(30); /// сверху state = STOP; // Останавливаемся } if(this->y() + 30 > 520){ this->setY(520 - 30); /// снизу state = STOP; // Останавливаемся } } } void Triangle::slotTarget(QPointF point) { // Определяем расстояние до цели target = point; QLineF lineToTarget(QPointF(0, 0), mapFromScene(target)); // Угол поворота в направлении к цели 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 setRotation(rotation() - angleToTarget * 180 /Pi); } else if (angleToTarget <= TwoPi && angleToTarget > Pi) { // Rotate right setRotation(rotation() + (angleToTarget - TwoPi )* (-180) /Pi); } state = GO; // Разрешаем идти }
Барлығы
Нәтижесінде сіз басты кейіпкердің қозғалысын Diablo сияқты RPG сияқты бір тінтуірмен басқаратын қолданба аласыз. Қосымшаның демонстрациясын бейне оқулықтан көре аласыз.
Жобаны zip мұрағатында жүктеп алу сілтемесі: QGraphicsObjectExample
Добрый день! Пожалуйста подскажите, что может означать ошибка - ~\customscene.h:17: ошибка: 'void CustomScene::signalTargetCoordinate(QPointF)' is protected
Добрый день! По какой-то причине у вас данный сигнал оказался в protected секции, а не в секции сигналов, то есть скорее всего он объявлен в заголовочнике так
Большое спасибо. Файл был Ваш, но принудительное добавление public: перед void signal эту ошибку устранило. Но есть еще ошибка -
~\widget.cpp:27: ошибка: no matching function for call to 'Widget::connect(CustomScene*&, void (CustomScene::*)(QPointF), Triangle*&, void (Triangle::*)(QPointF))'
Если можно помогите пожалуйста - я в Qt вторую неделю всего.
Скорее всего в одном из классов не объявлена нужная функция (слот или сигнал). Нужно, чтобы сигнатуры сигнала и слота совпадали в данном конкретном примере. А в одном из классов видимо отсутствует нужный метод ( сигнал или слот - почитайте эту статью, чтобы разобраться с теорией сигналов и слотов).
Большое спасибо, удачи Вам, здоровья, и всего доброго.
Добрый день! Еще раз спасибо за уроки. Все заработало, помог ваш урок 24. Public был ни при чем, нужно было две строки проекта с connect записать в стиле Qt 4.8.5. Правда - стрелками на клавиатуре треугольник двигается с точностью наоборот. Но это мелочи. Всего доброго.
Добрый день!
Что? А почему? Вы используете Qt 4.8?
Добрый день! Это требование руководства, увы!
Вы случаем не в ОНИИПе работаете?
Добрый день! Точно так, в НИИ, только не О...П. Остальное, к сожалению, не моя информация. Всего доброго.