Evgenii Legotckoi
Evgenii LegotckoiSept. 19, 2015, 9:33 p.m.

How to make game using Qt - Lesson 2. Animation game hero (2D)

In the second lesson we'll get to the next aspect of the writing of the game - the hero Animation. In the last lesson it was considered as a control object in the graphic scene, and the main character played a red triangle, but it is not very interesting. So we will turn the triangle into an animate object, namely the Fly, which will crawl under the control of the keyboard and move the legs when moving.

In this lesson, the adjustment made in the code triangle.h, triangle.cpp and just a little in widget.cpp file.

Animation Fly on steps

We describe an algorithm, which should be the rendering Flies:

  1. Initialize variable in the class constructor, which will be responsible for the number of legs position. There will be three positions fly legs.
  2. Initialize variable in the class constructor, which will be useful to consider the timer ticks of the game, then those ticks during which the fly moved. It is necessary to weed out those tics, in which there was no movement Flies Fly otherwise be constantly iterate legs, even if we do not manage Fly .
  3. Draw Fly in the paint method, choosing which of the provisions of the flies legs will be drawn.
  4. The slot that handles the event starting the game timer produces accumulation and reset the counter ticks useful game timer and depending on its value set position Flies legs, namely completely redraw the Fly with the correct position of the legs.

triangle.h

In this file add only two integer variables:

  • steps - position number of flies legs;
  • countForSteps - a counter for counting clock ticks for which the keyboard interface produced.
#ifndef TRIANGLE_H
#define TRIANGLE_H

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

#include <windows.h>

class Triangle : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit Triangle(QObject *parent = 0);
    ~Triangle();

signals:

public slots:
    void slotGameTimer(); 

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

private:
    qreal angle;     
    int steps;          // Number position flies legs
    int countForSteps;  // The counter for counting clock ticks for which we pressed the button

};

#endif // TRIANGLE_H

triangle.cpp

In general, changes were made completely whole file. The class constructor assigns values to two new variables. In the paint method is completely rendered the whole fly with three possible positions of the feet. To apply draw objects such as ellipses, lines and QPainterPath class objects.

In order to fly turned to the appropriate appearance, it was made a sketch of Flies, which coordinates the program was moved to the lesson code.

With the fun is present in the method slotGameTimer() , in which the tracking button states conducted countdown useful ticks, and if countForSteps variable equal to 4, 8, 12 and 16, chosen one of the provisions of the Flies legs and initiated redrawing Flies with this provision, legs.

#include "triangle.h"

Triangle::Triangle(QObject *parent) :
    QObject(parent), QGraphicsItem()
{
    angle = 0;      
    steps = 1;      // Set starting position flies legs
    countForSteps = 0;      // The counter for counting clock ticks for which we pressed the button
    setRotation(angle);     
}

Triangle::~Triangle()
{

}

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

void Triangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    // Draw the legs, without legs as a fly can not creep
    painter->setPen(QPen(Qt::black, 2));
    if(steps == 0){     // The first position of the legs
        // Left 1
        painter->drawLine(-24,-37,-22,-25);
        painter->drawLine(-22,-25,-17,-15);
        painter->drawLine(-17,-15,-10,-5);
        // Right 1
        painter->drawLine(37,-28,28,-18);
        painter->drawLine(28,-18,24,-8);
        painter->drawLine(24,-8,10,-5);

        // Left 2
        painter->drawLine(-35,-20,-25,-11);
        painter->drawLine(-25,-11,-14,-5);
        painter->drawLine(-14,-5,0,5);
        // Right 2
        painter->drawLine(37,-12,32,-4);
        painter->drawLine(32,-4,24,2);
        painter->drawLine(24,2,0,5);

        // Left 3
        painter->drawLine(-35,35,-26,24);
        painter->drawLine(-26,24,-16,16);
        painter->drawLine(-16,16,0,0);
        // Right 3
        painter->drawLine(37,26,32,17);
        painter->drawLine(32,17,24,8);
        painter->drawLine(24,8,0,0);
    } else if (steps == 1){     // The second position of the legs
        // Left 1
        painter->drawLine(-32,-32,-25,-22);
        painter->drawLine(-25,-22,-20,-12);
        painter->drawLine(-20,-12,-10,-5);
        // Right 1
        painter->drawLine(32,-32,25,-22);
        painter->drawLine(25,-22,20,-12);
        painter->drawLine(20,-12,10,-5);

        // Left 2
        painter->drawLine(-39,-15,-30,-8);
        painter->drawLine(-30,-8,-18,-2);
        painter->drawLine(-18,-2,0,5);
        // Right 2
        painter->drawLine(39,-15,30,-8);
        painter->drawLine(30,-8,18,-2);
        painter->drawLine(18,-2,0,5);

        // Left 3
        painter->drawLine(-39,30,-30,20);
        painter->drawLine(-30,20,-20,12);
        painter->drawLine(-20,12,0,0);
        // Right 3
        painter->drawLine(39,30,30,20);
        painter->drawLine(30,20,20,12);
        painter->drawLine(20,12,0,0);
    } else if (steps == 2){     // The third position of the legs
        // Left 1
        painter->drawLine(-37,-28,-28,-18);
        painter->drawLine(-28,-18,-24,-8);
        painter->drawLine(-24,-8,-10,-5);
        // Right 1
        painter->drawLine(24,-37,22,-25);
        painter->drawLine(22,-25,17,-15);
        painter->drawLine(17,-15,10,-5);

        // Left 2
        painter->drawLine(-37,-12,-32,-4);
        painter->drawLine(-32,-4,-24,2);
        painter->drawLine(-24,2,0,5);
        // Right 2
        painter->drawLine(35,-20,25,-11);
        painter->drawLine(25,-11,14,-5);
        painter->drawLine(14,-5,0,5);

        // Left 3
        painter->drawLine(-37,26,-32,17);
        painter->drawLine(-32,17,-24,8);
        painter->drawLine(-24,8,0,0);
        // Right 3
        painter->drawLine(35,35,26,24);
        painter->drawLine(26,24,16,16);
        painter->drawLine(16,16,0,0);
    }
    // Antennae
    QPainterPath path(QPointF(-5,-34));
    path.cubicTo(-5,-34, 0,-36,0,-30);
    path.cubicTo(0,-30, 0,-36,5,-34);
    painter->setBrush(Qt::NoBrush);
    painter->drawPath(path);

    painter->setPen(QPen(Qt::black, 1));
    // carcass
    painter->setBrush(Qt::black);
    painter->drawEllipse(-15, -20, 30, 50);
    // Head
    painter->drawEllipse(-15, -30, 30, 20);
    // eyes
    painter->setBrush(Qt::green);
    painter->drawEllipse(-15, -27, 12, 15);
    painter->drawEllipse(3, -27, 12, 15);

    // Left wing
    QPainterPath path2(QPointF(-10, -10));
    path2.cubicTo(-18, -10, -30, 10, -25, 35);
    path2.cubicTo(-25,35,-20,50,-15,40);
    path2.cubicTo(-15,40,0,20,-3,5 );
    path2.cubicTo(-3,5, -8,8,-10,-10);
    painter->setBrush(Qt::white);
    painter->drawPath(path2);

    // Right wing
    QPainterPath path3(QPointF(10, -10));
    path3.cubicTo(18, -10, 30, 10, 25, 35);
    path3.cubicTo(25,35,20,50,15,40);
    path3.cubicTo(15,40,0,20,3,5 );
    path3.cubicTo(3,5, 8,8,10,-10);
    painter->setBrush(Qt::white);
    painter->drawPath(path3);

    Q_UNUSED(option);
    Q_UNUSED(widget);
}

void Triangle::slotGameTimer()
{
    /* Check if pressed any of the object control buttons. Before you consider steps
     * */
    if(GetAsyncKeyState(VK_LEFT) ||
       GetAsyncKeyState(VK_RIGHT) ||
       GetAsyncKeyState(VK_UP) ||
       GetAsyncKeyState(VK_DOWN))
    {
        if(GetAsyncKeyState(VK_LEFT)){
            angle -= 5;        
            setRotation(angle); 
        }
        if(GetAsyncKeyState(VK_RIGHT)){
            angle += 5;        
            setRotation(angle); 
        }
        if(GetAsyncKeyState(VK_UP)){
            setPos(mapToParent(0, -2));    
        }
        if(GetAsyncKeyState(VK_DOWN)){
            setPos(mapToParent(0, 2));      
        }

        countForSteps++;
        if(countForSteps == 4){
            steps = 2;
            update(QRectF(-40,-50,80,100));
        } else if (countForSteps == 8){
            steps = 1;
            update(QRectF(-40,-50,80,100));
        } else if (countForSteps == 12){
            steps = 0;
            update(QRectF(-40,-50,80,100));
        } else if (countForSteps == 16) {
            steps = 1;
            update(QRectF(-40,-50,80,100));
            countForSteps = 0;
        }
    }

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

widget.cpp

To improve the smoothness of drawing was increased frequency of signal processing from the timer.

timer->start(1000 / 100);

Result

As a result, you should get a fly that the motion will cycle through the tabs.

A full list of articles in this series:

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
  • April 25, 2018, 3:16 p.m.

Здравствуйте,
Подскажите, почему муха может оставлять следы на игровой сцене?

Evgenii Legotckoi
  • April 25, 2018, 3:51 p.m.

Добрый день. Артефакты обычно остаются в том случае, если не обновляется полностью тот участок графической сцены, в котором находится муха. Скорее возможно boundingRect() написали не совсем правильно, если писали его самостоятельно для своего собственного варианта. Также как вариант можете запускать метод update() на графической сцене с указание того пространства, которое требуется перерисовать.

Илья Некрасов
  • March 28, 2020, 12:28 a.m.
  • (edited)

Здравствуйте, а можно, пожалуйста, ссылку на целые исходники, если есть?

Evgenii Legotckoi
  • March 28, 2020, 12:40 a.m.

Добрый день. В конце пятой статьи скачать можете.

J
  • May 20, 2023, 9:27 p.m.

Евгений, здравствуйте! Подскажите, а почему при нажатии одной клавиши переменная countForSteps увеличивается не на 1, на 4, ведь одно действие даёт увеличение этой переменной только на единицу? (стр.184 в файле triangle.cpp)

Evgenii Legotckoi
  • May 21, 2023, 3:57 p.m.

Добрый день. slotGameTimer срабатывает по таймеру и при каждой сработке countForSteps увеличивается на 1, это не зависит от нажатия клавиш, нажатая клавиша лишь определяет положение ножек, которое отрисовывается у игрового персонажа. В коде есть часть, которая отвечает за отрисовку и эта часть делает проверку на число, кратное 4, поэтому вам и показалось, что countForSteps увеличивается на 4, а на самом деле во время нажатя клавиши слот успевает вызываться 4 раза.

J
  • May 21, 2023, 8:49 p.m.
  • (edited)

Евгений, благодарю! Всё равно не совсем понимаю :( Если муха двигает ножками только при нажатии клавиш перемещение, то что, собственно, делает код со строк 184-198 в triangle.cpp? В этих строчках код опеределяет "характер" движения ножек в зависимости от направления движения мухи. Но я не очень понимаю как эта часть кода связана с разными нажатиями кнопок игроком, которые, собственно, и задают "характер" движения ножек. Ведь, как я понял, таймер работает не только во время нажатия клавиш, так? Плюс, не понимаю, почему слот за одно движение срабатывает 4 раза... Прошу прощение, немного запутался :(

Evgenii Legotckoi
  • May 25, 2023, 2:49 p.m.

Код на строчка 184-198 вызывает перерисовку области на каждый 4-й такт счётчика. По той логике не нужно перерисовывать объект постоянно, достаточно реже, чем выполняется игровой слот. А слот выполняется постоянно по таймеру. Потому, что как вы правильно поняли, таймер работает и тогда, когда клавиши не нажимаются. А сработка 4 раза или даже чаще - это потому что нажатие обычно дольше, чем длительность отсчёта таймера. Слот срабатывает по таймеру, таймер срабатывает постоянно. Почему именно 4 раза? - Потому, что это эмпирически подобранная величина.

J
  • May 26, 2023, 12:24 a.m.

Евгений, благодарю!

J
  • Aug. 3, 2023, 1:56 p.m.

Евгейни, скажите: а почему вы не использовали в этом проекте класс событий нажатия клавиш QKeyPressEvent? Это менее целесообразно в данном случае было?

Evgenii Legotckoi
  • Aug. 3, 2023, 2:43 p.m.

Да, менее целесообразно. Не подходит, поскольку не даёт обрабатывать постоянное нажатие кнопки достаточно простым способом.
Во всяком случае это первая причина, которая мне вспоминается. Я уже почти ничего не помню с момента написания этого поста, касательно этого примера.

J
  • Aug. 3, 2023, 3:19 p.m.
  • (edited)

Евгений, я новичок в Qt и решил попрактиковать в создании какой-нибудь простенькой игры (змейка, тетрис, морской бой), где присутствует постоянное нажатие клавиш. На ваш опытный взгляд, можно ли сказать, что в таких и похожих ситуациях лучше не влезать в класс обработки событий клавиатуры из-за его насыщенности, а просто прибегнуть к подключению возможностей Win32 как вы сделали в этой игре?

Evgenii Legotckoi
  • Aug. 8, 2023, 12:13 p.m.

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

Comments

Only authorized users can post comments.
Please, Log in or Sign up
1
  • 12333
  • July 18, 2024, 3:34 p.m.

Qt - Test 001. Signals and slots

  • Result:63points,
  • Rating points-1
1
  • 12333
  • July 18, 2024, 3:25 p.m.

C++ - Test 005. Structures and Classes

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

C++ - Test 005. Structures and Classes

  • Result:33points,
  • Rating points-10
Last comments
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 аналогично. Могу предположить, что из-за более новой верси…
k
kmssrFeb. 9, 2024, 5:43 a.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 9:30 p.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 7:38 p.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
Now discuss on the forum
F
FynjyJuly 22, 2024, 2:15 p.m.
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCTJune 25, 2024, 11 a.m.
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
Evgenii Legotckoi
Evgenii LegotckoiJune 25, 2024, 1:11 a.m.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
BlinCT
BlinCTMay 5, 2024, 3:46 p.m.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiMay 3, 2024, 12:07 a.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Follow us in social networks