Evgenii Legotckoi
Evgenii LegotckoiDec. 6, 2015, 9:47 a.m.

Qt/C++ - Lesson 033. Working with QGraphicsObject instead of QGraphicsItem

My attention was drawn to the fact that for working with signals and slots instead of a class inherited from QGraphicsItem and from QObject , you can use a class inherited from QGraphicsObject . Indeed, if a little rummage in the source QGraphicsObject , we find that it is a class inherited from QGraphicsItem and from QObject . That is also used multiple inheritance, but in this case all the bikes have written to us. Therefore, we try to work with this class as an example of the game mechanics.

Specifically, I propose to write a program in which we will move the hero mouse click on the graphic scene, as in any RPG like Diablo.

Project structure for work with QGraphicsObject

  • QGraphicsObjectExample.pro - the profile of the project;
  • main.cpp - the main source file;
  • widget.h - header file of the application window;
  • widget.cpp - file source code of the application window;
  • customscene.h - customized header graphic scene;
  • customscene.cpp - file source customized graphic scene ;
  • triangle.h - header file of hero class;
  • triangle.cpp - source file triangle class.

mainwindow.ui - QGraphicsObjectExample.pro - main.cpp

The shape of the main window unremarkable. It is only the object QGraphicsView. QGraphicsObjectExample and main.cpp files are created by default and is not modified.

widget.h

To implement the conceived, we need only a customized graphical scene and nothing else. Although it was quite possible to declare and locally within the Widget class's constructor.

#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

The Widget class constructor initializes the graphic scenes and the triangle object. And also connect the signal from the graphics signalTargetCoordinate with triangle slot scene. signalTargetCoordinate will transmit the coordinates of the mouse, which will need to move the triangle.

#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

To transfer the coordinates of the mouse on the graphic scene to be used signal, and it will be passed from mousePressEvent and mouseMoveEvent . At the same time signaling data will be made only in the event that was pressed left mouse button.

#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()); 
    }
}

triangle.h

This class inherit from QGraphicsObject class and do not use inheritance from the QObject class, because QGraphicsObject have inherited from him, as well as QGrapihcsItem . Further work with this object is also produced, as if we were working with a class, inherited from QGrapgicsItem .

Small moment compared to other articles about GameDev , such as the one where we simply track the movement of the mouse hero, it is that there is a state variable standing / walking.

It is necessary to stop the hero in case he rested into the wall or border territory and its point of arrival would be unattainable. And here it is also used by the game timer to the game slot that handles the process of movement of the main character.

But the slot receiving the coordinates of the target point handles turn the hero in the direction of the target.

#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));
        }
        /* Check the output of the field boundary
         * If the subject is beyond the set boundaries, then return it back
         * */
        if(this->x() - 30 < 0){
            this->setX(30);         /// left
            state = STOP;          
        }
        if(this->x() + 30 > 520){
            this->setX(520 - 30);   /// riht
            state = STOP;          
        }

        if(this->y() - 30 < 0){
            this->setY(30);         /// top
            state = STOP;          
        }
        if(this->y() + 30 > 520){
            this->setY(520 - 30);   /// bottom
            state = STOP;           
        }
    }
}

void Triangle::slotTarget(QPointF point)
{
    // Determine the distance to the target
    target = point;
    QLineF lineToTarget(QPointF(0, 0), mapFromScene(target));
    // The angle of rotation in the direction to the target
    qreal angleToTarget = ::acos(lineToTarget.dx() / lineToTarget.length());
    if (lineToTarget.dy() < 0)
        angleToTarget = TwoPi - angleToTarget;
    angleToTarget = normalizeAngle((Pi - angleToTarget) + Pi / 2);

    // Turn the hero to the goal
    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; 
}

Result

As a result, you will receive an application, which will control the movement of the hero with the help of only one mouse, if it's an RPG, similar to Diablo.

Link to the project download in zip-archive: QGraphicsObjectExample

Video

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

m
  • March 6, 2018, 8:08 a.m.

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

Evgenii Legotckoi
  • March 6, 2018, 8:47 a.m.

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

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

Большое спасибо. Файл был Ваш, но принудительное добавление 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
  • March 6, 2018, 10:01 a.m.

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

m
  • March 6, 2018, 10:40 a.m.

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

m
  • March 7, 2018, 5:40 a.m.

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

Evgenii Legotckoi
  • March 7, 2018, 5:43 a.m.

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

m
  • March 8, 2018, 2:34 a.m.

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

Evgenii Legotckoi
  • March 8, 2018, 3:24 a.m.

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

m
  • March 9, 2018, 2:20 a.m.

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

Comments

Only authorized users can post comments.
Please, Log in or Sign up
OI

C++ - Test 001. The first program and data types

  • Result:40points,
  • Rating points-8
AD

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:50points,
  • Rating points-4
m

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:80points,
  • Rating points4
Last comments
ИМ
Игорь МаксимовNov. 22, 2024, 10:51 p.m.
Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiNov. 1, 2024, 12:37 a.m.
Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEOct. 19, 2024, 6:19 p.m.
Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовOct. 5, 2024, 5:51 p.m.
Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5July 5, 2024, 9:02 p.m.
QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Now discuss on the forum
Evgenii Legotckoi
Evgenii LegotckoiJune 25, 2024, 1:11 a.m.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Nov. 15, 2024, 5:04 p.m.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectJune 4, 2022, 1:49 p.m.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimOct. 25, 2024, 7:10 p.m.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Follow us in social networks