Evgenii Legotckoi
Evgenii Legotckoi18 вересня 2015 р. 10:54

Як створити гру за допомогою Qt - Урок 1. Керування об'єктом

З цього уроку починається серія статей у тому, як написати гру на Qt. У попередній статті було розказано про систему позиціонування графічних елементів QGraphicsItem у графічній сцені QGraphicsScene. Був намальований трикутник і поміщений у центр графічної сцени, розміри якої були 500 на 500 пікселів. А тепер настав час цей трикутник пожвавити, а точніше почати ним управляти.

Складемо технічне завдання уроку:

  • У вікні розташовується графічна сцена з розмірами 500 на 500 пікселів (це вже зроблено у попередньому уроці);
  • У центрі графічної сцени знаходиться червоний трикутник (що також вже зроблено у минулому уроці);
  • Трикутник повинен переміщатися при натисканні клавіш зі стрілками Up, Down, Left, Right;
  • Трикутник не повинен виходити за межі графічної сцени, тобто має бути обмежений розмірами графічної сцени.

Примітка. У цьому проекті використовується WinAPI, тому проект застосовується для використання в операційній системі Windows, а для Linux і MacOS застосовується лише алгоритм, який використовується в даному уроці. Тому якщо Ви бажаєте написати гру під ці ОС, то необхідно використовувати бібліотеки цих ОС для асинхронної обробки натискання клавіш.


Структура проекта

  • Triangle.pro - профайл проекту, що створюється за замовчуванням і в даному проекті не вимагає коригування;
  • main.cpp - файл, з якого стартує додаток, в даному файлі викликається widget, в якому розташовуватиметься графічна сцена з трикутником, яким ми керуватимемо;
  • widget.h - заголовний файл, що викликається віджетом з графічною сценою;
  • widget.cpp - файл вихідних кодів віджету;
  • triangle.h - заголовний файл класу Трикутника , який успадкований від QGraphicsItem;
  • triangle.cpp - файл вихідних кодів класу Трикутник.

mainwindow.ui

У дизайнері інтерфейсів просто закидаємо QGraphicsView у віджет. Більше нічого не потрібне.

віджет.h

У цьому файлі лише оголошуємо графічну сцену, об'єкт трикутника, яким будемо керувати, а також таймер, за відліками якого буде проводитися перевірка стану клавіш клавіатури, якими ми керуватимемо трикутником на графічній сцені.

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QGraphicsScene>
#include <QShortcut>
#include <QTimer>

#include <triangle.h>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget      *ui;
    QGraphicsScene  *scene;     /// Объявляем графическую сцену
    Triangle        *triangle;  /// и треугольник
    QTimer          *timer;     /* Объявляем игровой таймер, благодаря которому
                                 * будет производиться изменения положения объекта на сцене
                                 * При воздействии на него клавишами клавиатуры
                                 * */
};

#endif // WIDGET_H

widget.cpp

У цьому файлі проводиться ініціалізація графічної сцени та її розмірів, а також у графічній сцені малюються межі поля, де буде переміщатися трикутник. Але ключовим моментом є ініціалізація таймера, по обробці сигналу якого буде проводитися зміни стану графічної сцени, а також будуть змінюватися координати розташування трикутника, при відстеженні стану клавіш клавіатури.

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->resize(600,600);          /// Задаем размеры виджета, то есть окна
    this->setFixedSize(600,600);    /// Фиксируем размеры виджета

    scene = new QGraphicsScene();   /// Инициализируем графическую сцену
    triangle = new Triangle();      /// Инициализируем треугольник

    ui->graphicsView->setScene(scene);  /// Устанавливаем графическую сцену в graphicsView
    ui->graphicsView->setRenderHint(QPainter::Antialiasing);    /// Устанавливаем сглаживание
    ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); /// Отключаем скроллбар по вертикали
    ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); /// Отключаем скроллбар по горизонтали

    scene->setSceneRect(-250,-250,500,500); /// Устанавливаем область графической сцены

    scene->addLine(-250,0,250,0,QPen(Qt::black));   /// Добавляем горизонтальную линию через центр
    scene->addLine(0,-250,0,250,QPen(Qt::black));   /// Добавляем вертикальную линию через центр

    /* Дополнительно нарисуем органичение территории в графической сцене */
    scene->addLine(-250,-250, 250,-250, QPen(Qt::black));
    scene->addLine(-250, 250, 250, 250, QPen(Qt::black));
    scene->addLine(-250,-250,-250, 250, QPen(Qt::black));
    scene->addLine( 250,-250, 250, 250, QPen(Qt::black));

    scene->addItem(triangle);   /// Добавляем на сцену треугольник
    triangle->setPos(0,0);      /// Устанавливаем треугольник в центр сцены

    /* Инициализируем таймер и вызываем слот обработки сигнала таймера
     * у Треугольника 20 раз в секунду.
     * Управляя скоростью отсчётов, соответственно управляем скоростью
     * изменения состояния графической сцены
     * */
    timer = new QTimer();
    connect(timer, &QTimer::timeout, triangle, &Triangle::slotGameTimer);
    timer->start(1000 / 50);
}

Widget::~Widget()
{
    delete ui;
}

трикутник.h

А ось тепер приступаємо до програмного коду, який відповідає за графічний об'єкт, яким ми керуватимемо. Клас успадковується від QObject для роботи з сигналами та слотами , а також від QGraphicsItem.

Саме в цьому файлі підключається заголовний файл windows.h для роботи з функціоналом

#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;    // Угол поворота графического объекта

};

#endif // TRIANGLE_H

triangle.cpp

Відмальовка трикутника взята з попереднього уроку з позиціювання графічних елементів у графічній сцені, а ось перемальовка в слоті, який оброблятиме сигнал від таймера, а також ініціалізація первинного повороту об'єкта вже є новим шматком коду.

Поворот об'єкта визначається в градусах змінної angle і встановлюється функцією setRotation() , яка була успадкована від QGraphicsItem . Також для відстеження стану кнопок клавіатури використовується функція з WinAPI, а саме GetAsyncKeyState(), яка за кодом кнопки визначає стан цієї кнопки. При кожному сигналі від об'єкта класу QTimer відбувається перевірка натиснутих клавіш і залежно від стану змінюється положення трикутника.

#include "triangle.h"

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

Triangle::~Triangle()
{

}

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

void Triangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
        QPolygon polygon;   /// Используем класс полигона, чтобы отрисовать треугольник
        /// Помещаем координаты точек в полигональную модель
        polygon << QPoint(0,-40) << QPoint(25,40) << QPoint(-25,40);
        painter->setBrush(Qt::red);     /// Устанавливаем кисть, которой будем отрисовывать объект
        painter->drawPolygon(polygon);  /// Рисуем треугольник по полигональной модели
        Q_UNUSED(option);
        Q_UNUSED(widget);
}

void Triangle::slotGameTimer()
{
    /* Поочерёдно выполняем проверку на нажатие клавиш
     * с помощью функции асинхронного получения состояния клавиш,
     * которая предоставляется WinAPI
     * */
    if(GetAsyncKeyState(VK_LEFT)){
        angle -= 10;        // Задаём поворот на 10 градусов влево
        setRotation(angle); // Поворачиваем объект
    }
    if(GetAsyncKeyState(VK_RIGHT)){
        angle += 10;        // Задаём поворот на 10 градусов вправо
        setRotation(angle); // Поворачиваем объект
    }
    if(GetAsyncKeyState(VK_UP)){
        setPos(mapToParent(0, -5));     /* Продвигаем объект на 5 пискселей вперёд
                                         * перетранслировав их в координатную систему
                                         * графической сцены
                                         * */
    }
    if(GetAsyncKeyState(VK_DOWN)){
        setPos(mapToParent(0, 5));      /* Продвигаем объект на 5 пискселей назад
                                         * перетранслировав их в координатную систему
                                         * графической сцены
                                         * */
    }

    /* Проверка выхода за границы поля
     * Если объект выходит за заданные границы, то возвращаем его назад
     * */
    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);        // снизу
    }
}

Примітка

Для того, щоб проект скомпілювався з комплектом збірки MSVC, додайте до файлу проекту наступні рядки:

win32-msvc*{
    LIBS += -luser32
}

Підсумок

В результаті виконаної роботи ми зробили перші кроки до того, щоб написати гру. А саме навчилися керувати об'єктом, тобто нашим героєм-трикутником, з яким ми також попрацюємо в майбутніх уроках написання першої нашої гри.

У відеоуроці подано додаткові коментарі та пояснення до проекту, а також продемонстровано роботу програми.

Повний список статей цього циклу:

Відеоурок

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

Вам це подобається? Поділіться в соціальних мережах!

M
  • 13 жовтня 2017 р. 06:19

Добрый день,
Подскажите пожалуйста, чем может быть вызвана ошибка
"C:\Android\Qt\Mark\untitled4\triangle.cpp:5: ошибка: undefined reference to `vtable for Triangle'" ?
В конструкторе указывает на наследуемый класс QGraphicsItem и деструктор Triangle.
Я сначала пытался код сам исправить, но ничего не вышло и я просто скопировал Ваш.
Скопировал triangle.h и triangle.cpp.
Что это может быть?

Evgenii Legotckoi
  • 13 жовтня 2017 р. 06:50

Отвратительная ошибка на самом деле.

В одном случае достаточно прописать Q_OBJECT.
В другом случае достаточно удалить build и пересобрать проект.
Один раз пришлось пересоздавать проект с нуля.
Проблема в том, что какие-то таблицы мок-файлов портятся. До сих пор порой до конца не понимаю, из-за чего такая проблема происходит. Иногда случается из-за добавления новых файлов в проект.
M
  • 13 жовтня 2017 р. 07:23

Вы как всегда правы, удалить билд и перезапустить было решением проблемы

ПД
  • 06 серпня 2019 р. 09:58

Добрый день.
Проблема следующая:
треугольник при перемещении не отрисовывается полностью, кроме того, на графической сцене остаются его следы, но если, например, свернуть приложение и заново его открыть, то треугольник отрисовывется как положено.

Evgenii Legotckoi
  • 06 серпня 2019 р. 14:17

Добрый день. Переопределили boundingRect? и переопределили ли его правильно? выглядит так, что либо boundingRect неправильно расчитывается, либо не вызывается update сцены.

Спасибо, не поставил минус в boundingRect

СГ
  • 27 листопада 2019 р. 10:21

Евгений здравствуйте!Только начинаю разбираться в QT и работаю в Unreal Engine 4(делаю игры и приложения).Можно ли их сравнивать или они для разных задач.И что не сделать в Unreal,что можно сделать в QT.Оба на с++,у обоих есть дизайнеры для интерфейса.В Unreal правда blueprints,а в QT Qml(я так понимаю это его плюс перед Unreal).На Qml можно писать красивый интерфейс,но в Unreal это делать проще?

Evgenii Legotckoi
  • 27 листопада 2019 р. 10:52
  • (відредаговано)

Добрый день

Они для разных задач.

  • Unreal Engine 4 - это всё-таки игры. Писать CAD систему или программу для клиентов по работе с базой данных я бы точно не стал, не для того это.
  • Qt - это различные приложения, от десктопных до мобильных, но точно не игры. Вернее игру написать можно на Qt, но я бы не стал писать что-то очень сложное. Для этого нужно брать игровой движок, тот же самый Unreal Engine 4.

В Qt вам для игры придётся написать собственный движок, который будет отвечать за физику и т.д. А в Unreal Engine 4 придётся писать очень много подготовительной логики для моделей данных (таблицы и т.д.). Ну либо использовать Felgo - фреймворк для написания приложений, который написан поверх Qt, они там сделали большую работу для мобильных приложений. В качестве движка используют cocos2d для игр.

Делать вёрстку приложения на QML определённо проще, якоря очень хорошая вещь. Когда я пробовал делать простую вёрстку в UE4 для меню, то мне якорей очень сильно не хватало, которые есть в QML.

blueprints - это конёчно жёсткое псевдопрограммирование )))) работать с ними можно, но я прекрасно понимал, когда пробовал себя в UE4, что логику, которую я писал в blueprints 8-10 блоками , на С++ я мог бы написать в две строки кода, которые были бы предельно понятны. Поэтому сразу начал искать варианты переноса логики в C++.

blueprints хороши для того, что требует 3D представления, но биты и байты ими перебирать - это самовредительство.

В общем сравнивать можно по ощущениям, но точно не по назначению. UE4 - игры, а Qt - это всё остальное.

СГ
  • 27 листопада 2019 р. 11:22

Евгений, спасибо за развёрнутый ответ!

kesh KD
  • 13 травня 2020 р. 15:28

"В дизайнере интерфейсов просто закидываем QGraphicsView в виджет. Больше ничего не требуется."

Для тупеньких, объясните пж (Qt Creator 4.10.2)

Evgenii Legotckoi
  • 13 травня 2020 р. 16:06

В Qt Creator двойным кликом открыть ui файл, и перетащить в виджет Graphics View. Потом выделить корневой виджет и нажать кнопку для создания GridLayoyt, чтобы Graphics View нормально позиционировался.

kesh KD
  • 14 травня 2020 р. 03:35
  • (відредаговано)

Спасибо, и пока пользуясь случаем. PushButton в основном для чего?

Evgenii Legotckoi
  • 14 травня 2020 р. 03:57

Это обычная кнопка, клик по этой кнопке можно привязать к какому угодно функционалу, от применения настроек до открытия диалога. Зависит от логики, которую в неё вкладывают.

Если у вас и дальше будут вопросы, то задавайте их на форуме, каждый в отдельной теме, чтобы они уже были логически структурированы и не было оффтопов.
Я просматриваю все вопросы, которые появляются на форуме и по возможности отвечаю на них. Форум

V
  • 08 грудня 2020 р. 09:44

Может вы мне подскажете, ибо я не могу понять и найти как эти строчки изменить под linux:

Evgenii Legotckoi
  • 28 січня 2021 р. 09:47

Под Linux Не подскажу, там всё несколько сложнее

b
  • 06 квітня 2022 р. 02:12

Привет, такой вопрос. Пишу игру, где отрисовываю абсолютно всё через QPainter. Спрайты анимации вывожу через DrawImage(). Какой из вариантов (QPainter и QGraphicsView) лучше в плане производительности? По поводу функционала понятно, но интересует именно скорость отрисовки, так как на средних смартфонах бывает падает FPS

Коментарі

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

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

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

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

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

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

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах