Evgenii Legotckoi
Evgenii LegotckoiҚыр. 19, 2015, 11:33 Т.Ж.

Qt тілінде ойынды қалай жазу керек - 2-сабақ. Ойын кейіпкерінің анимациясы (2D)

Екінші сабақта ойын жазудың келесі аспектісіне көшейік - Ойын кейіпкерінің анимациясы. Басты кейіпкер қызыл үшбұрыш болды, бірақ бұл өте қызықты емес. Сондықтан үшбұрышты анимациялық нысанға, атап айтқанда пернетақтаның басқаруымен жорғалап, қозғалған кезде аяқтарын қозғалтатын Ұшуға айналдырайық.

Бұл сабақта бағдарлама коды негізінен triangle.h, triangle.cpp файлдарында және widget.cpp. файлында өте аз түзетілді.

Анимацияны кезең-кезеңімен орындаңыз

Fly сызылатын алгоритмді сипаттайық:

  1. Біз класс конструкторында аяқтардың позиция нөміріне жауап беретін айнымалыны инициализациялаймыз. Барлығы шыбынның аяқтарының үш позициясы болады.
  2. Класс конструкторында ойын таймерінің пайдалы белгілерін, содан кейін Fly жылжытылған белгілерді санайтын айнымалы мәнді инициализациялаймыз. Бұл Шыбын қозғалысы болмаған кенелерді жою үшін қажет, әйтпесе Шыбын басқармасақ та, Шыбын үнемі аяқтарын жылжытады.
  3. Шыбынның аяқтарының қайсысы сызылатынын таңдап, бояу, әдісімен Шыбын суретін сал.
  4. Ойын таймерінің кері санақ оқиғасын өңдейтін ұяшықта ол ойын таймерінің пайдалы кенелерінің есептегішін жинақтайды және қалпына келтіреді және оның мәніне байланысты Ұшудың аяқтарының орнын орнатады, атап айтқанда , аяқтың қалаған күйімен Fly -ді толығымен қайта сызады.

үшбұрыш.сағ

Бұл файлға тек екі бүтін айнымалыны қосамыз:

  • қадамдар - шыбынның аяқтарының позициясының нөмірі;
  • countForSteps - пернетақта пернелері басылған таймер белгілерін санауға арналған есептегіш.
#ifndef TRIANGLE\_H
#define TRIANGLE\_H

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

/* Подключаем библиотеку, отвечающую за использование WinAPI
 * Данная библиотека необходима для асинхронной проверки состояния клавиш
 * */
#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;          // Номер положения ножек мухи
    int countForSteps;  // Счётчик для отсчета тиков таймера, при которых мы нажимали на кнопки

};

#endif // TRIANGLE\_H

triangle.cpp

Жалпы, өзгерістер бүкіл файлға әсер етті. Класс конструкторы екі жаңа айнымалыға мәндер тағайындайды. Ал бояу әдісінде бүкіл Шыбын аяқтың үш ықтимал позициясымен толығымен сызылады. Сурет салу үшін эллипс, сызықтар және QPainterPath. класының объектілері сияқты нысандар пайдаланылады.

Шыбынның сәйкес келбеті бар болуы үшін координаталар бойынша сабақтың бағдарламалық кодына көшірілген Шыбынның эскизі жасалды.

Ең қызығы slotGameTimer() әдісінде бар, бұл әдісте түймелердің күйін бақылау кезінде пайдалы белгілер есептеледі және countForSteps айнымалысы 4, 8, 12-ге тең болса. және 16, аяқтардың позицияларының бірі таңдалады Шыбындар және аяқтардың осы қалпымен Шыбындардың қайта сызуын бастайды.

#include "triangle.h"

Triangle::Triangle(QObject *parent) :
    QObject(parent), QGraphicsItem()
{
    angle = 0;      // Задаём угол поворота графического объекта
    steps = 1;      // Задаём стартовое положение ножек мухи
    countForSteps = 0;      // Счётчик для отсчета тиков таймера, при которых мы нажимали на кнопки
    setRotation(angle);     // Устанавливаем угол поворота графического объекта
}

Triangle::~Triangle()
{

}

QRectF Triangle::boundingRect() const
{
    return QRectF(-40,-50,80,100);   /// Ограничиваем область, в которой лежит треугольник
}

void Triangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    // Рисуем ножки, без ножек же муха не сможет ползать
    painter->setPen(QPen(Qt::black, 2));
    if(steps == 0){     // Первое положение ножек
        // 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){     // Второе положение ножек
        // 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){     // Третье положение ножек
        // 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);
    }
    // Усики
    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));
    // Тушка
    painter->setBrush(Qt::black);
    painter->drawEllipse(-15, -20, 30, 50);
    // Голова
    painter->drawEllipse(-15, -30, 30, 20);
    // Глазища
    painter->setBrush(Qt::green);
    painter->drawEllipse(-15, -27, 12, 15);
    painter->drawEllipse(3, -27, 12, 15);

    // Левое крылище
    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);

    // Правое крылище
    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()
{
    /* Проверяем, нажата ли была какая-либо из кнопок управления объектом.
     * Прежде чем считать шажки
     * */
    if(GetAsyncKeyState(VK\_LEFT) ||
       GetAsyncKeyState(VK\_RIGHT) ||
       GetAsyncKeyState(VK\_UP) ||
       GetAsyncKeyState(VK\_DOWN))
    {
        /* Поочерёдно выполняем проверку на нажатие клавиш
         * с помощью функции асинхронного получения состояния клавиш,
         * которая предоставляется WinAPI
         * */
        if(GetAsyncKeyState(VK\_LEFT)){
            angle -= 5;        // Задаём поворот на 5 градусов влево
            setRotation(angle); // Поворачиваем объект
        }
        if(GetAsyncKeyState(VK\_RIGHT)){
            angle += 5;        // Задаём поворот на 5 градусов вправо
            setRotation(angle); // Поворачиваем объект
        }
        if(GetAsyncKeyState(VK\_UP)){
            setPos(mapToParent(0, -2));     /* Продвигаем объект на 5 пискселей вперёд
                                             * перетранслировав их в координатную систему
                                             * графической сцены
                                             * */
        }
        if(GetAsyncKeyState(VK\_DOWN)){
            setPos(mapToParent(0, 2));      /* Продвигаем объект на 5 пискселей назад
                                             * перетранслировав их в координатную систему
                                             * графической сцены
                                             * */
        }

        // Двигаем ножками, Dance, dance, Baby !!!
        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

Көрсетудің бірқалыптылығын жақсарту үшін таймерден сигнал жиілігі ұлғайтылды.

timer->start(1000 / 100);

Барлығы

Нәтижесінде сіз қозғалған кезде табандарына тиетін Шыбын алуыңыз керек. Сондай-ақ осы мақаладағы бейне оқулықта осы сабақтың нәтижесінің демонстрациясын көре аласыз.

Осы сериядағы мақалалардың толық тізімі:

Бейне оқулық

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

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

M
  • Сәуір 25, 2018, 5:16 Т.Ж.

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

Evgenii Legotckoi
  • Сәуір 25, 2018, 5:51 Т.Ж.

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

Илья Некрасов
  • Наурыз 27, 2020, 2:28 Т.Қ.
  • (өңделген)

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

Evgenii Legotckoi
  • Наурыз 27, 2020, 2:40 Т.Қ.

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

J
  • Мамыр 20, 2023, 11:27 Т.Ж.

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

Evgenii Legotckoi
  • Мамыр 21, 2023, 5:57 Т.Ж.

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

J
  • Мамыр 21, 2023, 10:49 Т.Ж.
  • (өңделген)

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

Evgenii Legotckoi
  • Мамыр 25, 2023, 4:49 Т.Ж.

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

J
  • Мамыр 25, 2023, 2:24 Т.Қ.

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

J
  • Там. 3, 2023, 3:56 Т.Ж.

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

Evgenii Legotckoi
  • Там. 3, 2023, 4:43 Т.Ж.

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

J
  • Там. 3, 2023, 5:19 Т.Ж.
  • (өңделген)

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

Evgenii Legotckoi
  • Там. 8, 2023, 2:13 Т.Ж.

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

Пікірлер

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

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:50ұпай,
  • Бағалау ұпайлары-4
m
  • molni99
  • Қаз. 26, 2024, 1:37 Т.Ж.

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:80ұпай,
  • Бағалау ұпайлары4
m
  • molni99
  • Қаз. 26, 2024, 1:29 Т.Ж.

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:20ұпай,
  • Бағалау ұпайлары-10
Соңғы пікірлер
i
innorwallҚар. 13, 2024, 11:03 Т.Қ.
Qt тілінде ойын қалай жазылады - 3-сабақ. Басқа объектілермен әрекеттесу what is priligy tablets What happens during the LASIK surgery process
i
innorwallҚар. 13, 2024, 8:09 Т.Қ.
C++ файлдарының ішінде CMakeLists.txt ішінде жарияланған айнымалы мәндерді пайдалану where can i buy priligy online safely Tom Platz How about things like we read about in the magazines like roid rage and does that really
i
innorwallҚар. 11, 2024, 10:12 Т.Қ.
Django - Оқулық 055. Автоматты толтыру өрісі функциясын қалай жазу керек Freckles because of several brand names retin a, atralin buy generic priligy
i
innorwallҚар. 11, 2024, 6:23 Т.Қ.
QML - Сабақ 035. C++ қолданбай QML тілінде сандарды пайдалану priligy cvs 24 Together with antibiotics such as amphotericin B 10, griseofulvin 11 and streptomycin 12, chloramphenicol 9 is in the World Health Organisation s List of Essential Medici…
i
innorwallҚар. 11, 2024, 3:50 Т.Қ.
Qt/C++ - 052-сабақ. Qt аудио ойнатқышын AIMP стилінде теңшеу It decreases stress, supports hormone balance, and regulates and increases blood flow to the reproductive organs buy priligy online safe Promising data were reported in a PDX model re…
Енді форумда талқылаңыз
i
innorwallҚар. 13, 2024, 6:52 Т.Қ.
добавить qlineseries в функции PMID 35774217 Free PMC article priligy cvs
i
innorwallҚар. 11, 2024, 10:55 Т.Ж.
Всё ещё разбираюсь с кешем. priligy walgreens levitra dulcolax carbs The third ring was found to be made up of ultra relativistic electrons, which are also present in both the outer and inner rings
9
9AnonimҚаз. 25, 2024, 9:10 Т.Ж.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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