Evgenii Legotckoi
Evgenii LegotckoiЖел. 6, 2015, 9:47 Т.Ж.

Qt/C++ - 033-сабақ. QGraphicsItem орнына QGraphicsObject-пен жұмыс істеу

Менің назарымды [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

Бейне оқулық

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

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

m
  • Наурыз 6, 2018, 8:08 Т.Ж.

Добрый день! Пожалуйста подскажите, что может означать ошибка - ~\customscene.h:17: ошибка: 'void CustomScene::signalTargetCoordinate(QPointF)' is protected

Evgenii Legotckoi
  • Наурыз 6, 2018, 8:47 Т.Ж.

Добрый день! По какой-то причине у вас данный сигнал оказался в protected секции, а не в секции сигналов, то есть скорее всего он объявлен в заголовочнике так

protected:
    void signalTargetCoordinate(QPointF point);
А не так
public:
    void signalTargetCoordinate(QPointF point);
А вы видимо пытаетесь вызвать его извне объекта. Или что-то типо того...
m
  • Наурыз 6, 2018, 9:51 Т.Ж.

Большое спасибо. Файл был Ваш, но принудительное добавление public: перед void signal эту ошибку устранило. Но есть еще ошибка -

~\widget.cpp:27: ошибка: no matching function for call to 'Widget::connect(CustomScene*&, void (CustomScene::*)(QPointF), Triangle*&, void (Triangle::*)(QPointF))'

Если можно помогите пожалуйста - я в Qt вторую неделю всего.

Evgenii Legotckoi
  • Наурыз 6, 2018, 10:01 Т.Ж.

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

m
  • Наурыз 6, 2018, 10:40 Т.Ж.

Большое спасибо, удачи Вам, здоровья, и всего доброго.

m
  • Наурыз 7, 2018, 5:40 Т.Ж.

Добрый день! Еще раз спасибо за уроки. Все заработало, помог ваш урок 24. Public был ни при чем, нужно было две строки проекта с connect записать в стиле Qt 4.8.5. Правда - стрелками на клавиатуре треугольник двигается с точностью наоборот. Но это мелочи. Всего доброго.

Evgenii Legotckoi
  • Наурыз 7, 2018, 5:43 Т.Ж.

Добрый день!
Что? А почему? Вы используете Qt 4.8?

m
  • Наурыз 8, 2018, 2:34 Т.Ж.

Добрый день! Это требование руководства, увы!

Evgenii Legotckoi
  • Наурыз 8, 2018, 3:24 Т.Ж.

Вы случаем не в ОНИИПе работаете?

m
  • Наурыз 9, 2018, 2:20 Т.Ж.

Добрый день! Точно так, в НИИ, только не О...П. Остальное, к сожалению, не моя информация. Всего доброго.

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
Г

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

  • Нәтиже:66ұпай,
  • Бағалау ұпайлары-1
t

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

  • Нәтиже:33ұпай,
  • Бағалау ұпайлары-10
t

Qt - Тест 001. Сигналы и слоты

  • Нәтиже:52ұпай,
  • Бағалау ұпайлары-4
Соңғы пікірлер
G
GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssrАқп. 8, 2024, 6:43 Т.Қ.
Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
F
FynjyШілде 22, 2024, 4:15 Т.Ж.
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCTМаусым 25, 2024, 1 Т.Ж.
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
BlinCT
BlinCTМамыр 5, 2024, 5:46 Т.Ж.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Бізді әлеуметтік желілерде бақылаңыз