Реклама

Как написать игру на Qt - Урок 2. Анимация героя игры (2D)

TutorialQtGameDev, QPainter, Qt, анимация, написать игру538

Во втором уроке подойдём к следующему аспекту написания игры - Анимация героя игры. В прошлом уроке было рассмотрено, как управлять объектом на графической сцене, и главным героем выступал красный треугольник, но это не очень интересно. Поэтому превратим треугольник в одушевленный объект, а именно в Муху, которая будет ползать под управлением клавиатуры и двигать ножками при движении.

В данном уроке корректировка программного кода производилась в главным образом в файлах triangle.h, triangle.cpp и самую малость в файле widget.cpp.

Анимация Мухи по шагам

Опишем  алгоритм, по которому будет производиться отрисовка Мухи:

  1. Инициализируем в конструкторе класса переменную, которая будет отвечать за номер положения ножек. Всего будет три положения ножек мухи.
  2. Инициализируем в конструкторе класса переменную, которая будет считать полезные тики игрового таймера, то те тики, во время которых Муха передвигалась. Это необходимо, чтобы отсеять те тики, в которых не было движения Мухи, иначе Муха будет постоянно перебирать ножками, даже если мы не будем управлять Мухой.
  3. Отрисовываем Муху в методе paint, выбирая, какое из положений ножек мухи будет отрисовываться.
  4. В слоте, который обрабатывает событие отсчета игрового таймера производит накопление и обнуление счетчика полезных тиков игрового таймера и в зависимости от его величины устанавлвиваем положение ножек Мухи, а именно полностью перерисовываем Муху с нужным положением ножек.

triangle.h

В данный файл добавляем лишь две целочисленные переменные:

  • steps - номер положения ножек мухи;
  • 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

В целом изменения коснулись полностью всего файла. В конструкторе класса присваиваются значения двум новым переменным. А в методе paint полностью отрисовывается вся Муха с тремя возможными положениями ножек. Для отрисовки применяются такие объекты, как эллипсы, линии и объекты класса 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);

Итог

В результате у Вас должна получиться Муха, которая при движении будет перебирать лапками. Также демонстрацию результата данного урока Вы можете посмотреть в видеоуроке по данной статье.

Полный список статей данного цикла:

Видеоурок

 

@

Реклама

Реклама

Комментарии

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь

Реклама

Реклама