Evgenii Legotckoi
28 сентября 2015 г. 22:29

GameDev на Qt - Урок 1. Отслеживание перемещения мыши в QGraphicsScene

Все помнят игру Crimsonland ? Там нужно было валить монстров штабелями. Для перемещения по игровому полю мы использовали клавиши W, A, S, D, а для направления стрельбы курсор мыши , за которым производилось отслеживание. Так вот, сам по себе механизм работы этого самого отслеживания мыши предельно прост. Реализаций может быть множество, в зависимости от используемых инструментов в программировании, но если говорить об обычном QGraphicsScene , то позвольте продемонстрировать Вам мой вариант реализации подобного механизма.

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

В этом проекте помимо основных файлов используется два дополнительных класса. Первый - это кастомизированный QGraphicsScene , который будет производить отслеживание положения курсора и передавать информацию о его положении, а второй - это главный герой, наш любимый Красный Треугольник , которым мы будем управлять с помощью клавиш W, A, S, D.

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

  • TargetMotion.pro - Профайл проекта;
  • widget.h - Заголовочный файл основного окна приложения;
  • widget.cpp - Файл исходных кодов основного окна приложения;
  • triangle.h - Заголовочный файл главного героя Красного Треугольника;
  • triangle.cpp - Файл исходных кодов главного героя Красного Треугольника;
  • customscene.h - Заголовочный файл кастомизированной графической сцены;
  • customscene.cpp - Файл исходных кодов кастомизированной графической сцены;
  • cursor.qrc - файл ресурсов, в котором содержится кастомизированный курсор мыши.

Пишем отслеживание курсора мыши

widget.ui

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

customscene.h

Всё, что нужно сделать в данном заголовочном файле, это лишь объявить метод для отслеживания перемещения мыши mouseMoveEvent() и сигнал, в котором будут передаваться координаты положения мыши, не забывая отнаследовать класс от QGraphicsScene.

  1. #ifndef CUSTOMSCENE_H
  2. #define CUSTOMSCENE_H
  3.  
  4. #include <QObject>
  5. #include <QGraphicsScene>
  6. #include <QGraphicsSceneMouseEvent>
  7.  
  8. class CustomScene : public QGraphicsScene
  9. {
  10. Q_OBJECT
  11. public:
  12. explicit CustomScene(QObject *parent = 0);
  13. ~CustomScene();
  14.  
  15. signals:
  16. // Сигнал для передачи координат положения курсора мыши
  17. void signalTargetCoordinate(QPointF point);
  18.  
  19. public slots:
  20.  
  21. private:
  22. // Функция, в которой производится отслеживание положения мыши
  23. void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
  24. };
  25.  
  26. #endif // CUSTOMSCENE_H

customscene.cpp

Вы не поверите, но в файле исходных кодов Мы всего лишь вызываем сигнал с координатами в методе mouseMoveEvent.

  1. #include "customscene.h"
  2.  
  3. CustomScene::CustomScene(QObject *parent) :
  4. QGraphicsScene()
  5. {
  6. Q_UNUSED(parent);
  7. }
  8.  
  9. CustomScene::~CustomScene()
  10. {
  11.  
  12. }
  13.  
  14. void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  15. {
  16. emit signalTargetCoordinate(event->scenePos());
  17. }

triangle.h

А теперь объявим все объекты и функции, необходимые для правильной отрисовки нашего героя и реализации отслеживания курсора в игре. Для более полного представления о работе данного класса и его истоков рекомендую ознакомиться с циклом статей "Как написать игру на Qt" , который представлен в разделе Qt Уроки .

Примечание . Для обработки нажатий кнопок используются библиотеки WinAPI .

  1. #ifndef TRIANGLE_H
  2. #define TRIANGLE_H
  3.  
  4. #include <QObject>
  5. #include <QGraphicsItem>
  6. #include <QPainter>
  7. #include <QPolygon>
  8. #include <QTimer>
  9.  
  10. #include <windows.h>
  11.  
  12. class Triangle : public QObject, public QGraphicsItem
  13. {
  14. Q_OBJECT
  15. public:
  16. explicit Triangle(QObject *parent = 0);
  17. ~Triangle();
  18.  
  19. signals:
  20.  
  21. public slots:
  22. // Слот для получения данных о положении курсора
  23. void slotTarget(QPointF point);
  24.  
  25. private:
  26. QRectF boundingRect() const;
  27. void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
  28.  
  29. private slots:
  30. void slotGameTimer(); // Игровой слот
  31.  
  32. private:
  33. QTimer *gameTimer; // Игровой таймер
  34. QPointF target; // Положение курсора
  35. };
  36.  
  37. #endif // TRIANGLE_H

triangle.cpp

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

  1. #include "triangle.h"
  2. #include <math.h>
  3.  
  4. static const double Pi = 3.14159265358979323846264338327950288419717;
  5. static double TwoPi = 2.0 * Pi;
  6.  
  7. static qreal normalizeAngle(qreal angle)
  8. {
  9. while (angle < 0)
  10. angle += TwoPi;
  11. while (angle > TwoPi)
  12. angle -= TwoPi;
  13. return angle;
  14. }
  15.  
  16. Triangle::Triangle(QObject *parent) :
  17. QObject(parent), QGraphicsItem()
  18. {
  19. setRotation(0); // Устанавливаем исходный разворот треугольника
  20. gameTimer = new QTimer(); // Инициализируем игровой таймер
  21. // Подключаем сигнал от таймера и слоту обработки игрового таймера
  22. connect(gameTimer, &QTimer::timeout, this, &Triangle::slotGameTimer);
  23. gameTimer->start(10); // Стартуем таймер
  24. target = QPointF(0,0); // Устанавливаем изначальное положение курсора
  25. }
  26.  
  27. Triangle::~Triangle()
  28. {
  29.  
  30. }
  31.  
  32. QRectF Triangle::boundingRect() const
  33. {
  34. return QRectF(-20,-30,40,60);
  35. }
  36.  
  37. void Triangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
  38. {
  39. /* Отрисовка треугольника
  40. * */
  41. QPolygon polygon;
  42. polygon << QPoint(0,-30) << QPoint(20,30) << QPoint(-20,30);
  43. painter->setBrush(Qt::red);
  44. painter->drawPolygon(polygon);
  45.  
  46. Q_UNUSED(option);
  47. Q_UNUSED(widget);
  48. }
  49.  
  50. void Triangle::slotTarget(QPointF point)
  51. {
  52. // Определяем расстояние до цели
  53. target = point;
  54. QLineF lineToTarget(QPointF(0, 0), mapFromScene(target));
  55. // Угол поворота в направлении к цели
  56. qreal angleToTarget = ::acos(lineToTarget.dx() / lineToTarget.length());
  57. if (lineToTarget.dy() < 0)
  58. angleToTarget = TwoPi - angleToTarget;
  59. angleToTarget = normalizeAngle((Pi - angleToTarget) + Pi / 2);
  60.  
  61. /* В Зависимости от того, слева или справа находится Цель от Героя,
  62. * устанавливаем направление поворота Героя в данном тике таймера
  63. * */
  64. if (angleToTarget >= 0 && angleToTarget < Pi) {
  65. // Rotate left
  66. setRotation(rotation() - angleToTarget * 180 /Pi);
  67. } else if (angleToTarget <= TwoPi && angleToTarget > Pi) {
  68. // Rotate right
  69. setRotation(rotation() + (angleToTarget - TwoPi )* (-180) /Pi);
  70. }
  71. }
  72.  
  73. void Triangle::slotGameTimer()
  74. {
  75. /* Перемещаем треугольник в зависимости от нажатых кнопок
  76. * */
  77. if(GetAsyncKeyState('A')){
  78. this->setX(this->x() - 2);
  79. }
  80. if(GetAsyncKeyState('D')){
  81. this->setX(this->x() + 2);
  82. }
  83. if(GetAsyncKeyState('W')){
  84. this->setY(this->y() - 2);
  85. }
  86. if(GetAsyncKeyState('S')){
  87. this->setY(this->y() + 2);
  88. }
  89.  
  90. /* Проверка выхода за границы поля
  91. * Если объект выходит за заданные границы, то возвращаем его назад
  92. * */
  93. if(this->x() - 30 < 0){
  94. this->setX(30); // слева
  95. }
  96. if(this->x() + 30 > 500){
  97. this->setX(500 - 30); // справа
  98. }
  99.  
  100. if(this->y() - 30 < 0){
  101. this->setY(30); // сверху
  102. }
  103. if(this->y() + 30 > 500){
  104. this->setY(500 - 30); // снизу
  105. }
  106.  
  107. QLineF lineToTarget(QPointF(0, 0), mapFromScene(target));
  108. // Угол поворота в направлении к цели
  109. qreal angleToTarget = ::acos(lineToTarget.dx() / lineToTarget.length());
  110. if (lineToTarget.dy() < 0)
  111. angleToTarget = TwoPi - angleToTarget;
  112. angleToTarget = normalizeAngle((Pi - angleToTarget) + Pi / 2);
  113.  
  114. /* В Зависимости от того, слева или справа находится Цель от Героя,
  115. * устанавливаем направление поворота Героя в данном тике таймера
  116. * */
  117. if (angleToTarget >= 0 && angleToTarget < Pi) {
  118. // Rotate left
  119. setRotation(rotation() - angleToTarget * 180 /Pi);
  120. } else if (angleToTarget <= TwoPi && angleToTarget > Pi) {
  121. // Rotate right
  122. setRotation(rotation() + (angleToTarget - TwoPi )* (-180) /Pi);
  123. }
  124. }

widget.h

А теперь необходимо всё свести в игровом ядре, которое будет находится в классе главного окна приложения.

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3.  
  4. #include <QWidget>
  5. #include <QGraphicsScene>
  6. #include <QGraphicsItem>
  7.  
  8. #include <triangle.h>
  9. #include <customscene.h>
  10.  
  11. namespace Ui {
  12. class Widget;
  13. }
  14.  
  15. class Widget : public QWidget
  16. {
  17. Q_OBJECT
  18.  
  19. public:
  20. explicit Widget(QWidget *parent = 0);
  21. ~Widget();
  22.  
  23. private:
  24. Ui::Widget *ui;
  25. CustomScene *scene; // Объявляем графическую сцену
  26. Triangle *triangle; // Объявляем треугольник
  27. };
  28.  
  29. #endif // WIDGET_H

widget.cpp

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

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3.  
  4. Widget::Widget(QWidget *parent) :
  5. QWidget(parent),
  6. ui(new Ui::Widget)
  7. {
  8. // Устанавливаем параметры окна приложения
  9. this->resize(600,600);
  10. this->setFixedSize(600,600);
  11.  
  12. ui->setupUi(this);
  13. scene = new CustomScene(); // Инициализируем кастомизированную сцену
  14.  
  15. ui->graphicsView->setScene(scene); /// Устанавливаем графическую сцену в graphicsView
  16. ui->graphicsView->setRenderHint(QPainter::Antialiasing); /// Устанавливаем сглаживание
  17. ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); /// Отключаем скроллбар по вертикали
  18. ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); /// Отключаем скроллбар по горизонтали
  19.  
  20. scene->setSceneRect(0,0,500,500); // Устанавливаем размеры графической сцены
  21.  
  22. // Создаем кастомизированный курсор из ресурсного файла
  23. QCursor cursor = QCursor(QPixmap(":/cursor/cursorTarget.png"));
  24. ui->graphicsView->setCursor(cursor); // Устанавливаем курсор в QGraphicsView
  25. triangle = new Triangle(); // Инициализируем треугольник
  26. triangle->setPos(250,250); // Устанавливаем стартовую позицию треугольника
  27. scene->addItem(triangle); // Добавляем треугольник на графическую сцену
  28.  
  29. /* Разрешаем отслеживание положение курсора мыши
  30. * без необходимости нажатия на кнопки мыши
  31. * Применяем это свойство именно для QGraphicsView,
  32. * в котором установлена графическая сцена
  33. * */
  34. ui->graphicsView->setMouseTracking(true);
  35.  
  36. // Подключаем сигнал от графической сцены к слоту треугольника
  37. connect(scene, &CustomScene::signalTargetCoordinate, triangle, &Triangle::slotTarget);
  38. }
  39.  
  40. Widget::~Widget()
  41. {
  42. delete ui;
  43. }

Итог

В результате вы получите треугольник на графической сцене, который будет перемещаться по ней под воздействием клавиш W, A, S, D и всегда смотреть в сторону курсора мыши, если тот находится в пределах графической сцены.

Демонстрация данного примера приведена в видеоуроке по данной статье.

Видеоурок

Вам это нравится? Поделитесь в социальных сетях!

r
  • 10 февраля 2018 г. 21:12

А где можно найти полный код проекта (проектов)? У меня ошибки линковки типа ( LNK2019: ссылка на неразрешенный внешний символ "public: void __cdecl Triangle::slotTarget(class QPointF)" (?slotTarget@Triangle@@QEAAXVQPointF@@@Z) в функции "public: __cdecl Widget::Widget(class QWidget *)" ).  И может кто подскажет как поправить?

Evgenii Legotckoi
  • 11 февраля 2018 г. 20:21

Вот в этой статье в конце есть полный проект.

r
  • 11 февраля 2018 г. 21:45

спасибо

r
  • 11 февраля 2018 г. 21:47

а если например я хочу сделать вместо треугольников - текстурки, я могу это сделать в этом виджете Qt? или надо брать что то другое?

Evgenii Legotckoi
  • 11 февраля 2018 г. 22:22

Для отрисовки используется объект класса QPainter  методе paint у треугольника. А этот класс QPainter имеет метод drawPixmap(), который может отрисовывать изображение из png файла например. Поэтому да, можете лишь переписать метод paint, и отрисовывать текстуры.

r
  • 11 февраля 2018 г. 22:28

еще такой вопрос. у меня Qt ругается на GetAsyncKeyState (ошибка линковки). Надо либу какую то подключить? 2) а есть такая функция н кросс платформенная (зачем в таком достаточно высокоуровневом фреймворке использовать winApi?)?

Evgenii Legotckoi
  • 11 февраля 2018 г. 22:35

Для MSVC пропишите в pro файле проекта следующие строчки

win32-msvc*{
    LIBS += -luser32
}
Видите ли, фреймворк может и достаточно высокоуровневый, но некоторый особенности работы в нём не реализованы в кросплатформенном варианте.
Например, нет тех же самых глобальных хоткеев . Я сам занимался реализацией подобного функционала для одного своего проекта.
 
Поэтому и проверка статуса клавиш тоже из этого разряда. А если говорить конкретнее, то проблема в том, что состояния клавиш проходят через цепочку событий
Evgenii Legotckoi
  • 11 февраля 2018 г. 22:39

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

r
  • 11 февраля 2018 г. 22:50

я собираю под win64.    LIBS += -luser32 и  LIBS += -luser64 не помогает

Evgenii Legotckoi
  • 12 февраля 2018 г. 12:22

Вы вообще что-то своё уже пишите, или как? Я скачал проект, запустил его на работе на Win64 и он работает.

А так по факту заголовочник нужен
#include <windows.h>
r
  • 12 февраля 2018 г. 23:39

Да нет. Чет у меня с qt только проблемы. Ваш проект так в qt не смог собрать (из за GetAsyncKeyState). Собрал его в visual studio, но там почему то пришлось в ручную кидать .dll в каталог (хотя путь добавлял) (может тут что изменить см скрин https://yadi.sk/i/-D9CsBhh3SL2eQ ).

r
  • 13 февраля 2018 г. 0:01

Еще пару вопросов по code-style

1)override в qt не используется?
2)почему 0 а не nullptr ?
3)макрос Q_UNUSED. Почему нельзя просто не давать переменным имя и тд?
4)Зачем объявлять что то если там пустое тело?  (2)
5)Зачем прописывать вызов конструктора по умолчанию? (2)
6)Почему иногда не чистится динамическая память? это ошибка? не лучше ли использовать std::unique_ptr указатели вместо обычных?
7)почему нужно объявлять именно указатели на объекты и вызывать new в конструкторе, а не просто делать объекты членами класса (а не их указатели)?
8) такие штуки как public slots: , где  slots макрос который раскрывается в ничего. Для чего нужны? (просто для оформления)?
9) такие штуки как emit. Это пустые макросы. Они для оформления, или код сначала прогоняется в компиляторе qt который понимает эти "ключевые слова" ?

Evgenii Legotckoi
  • 13 февраля 2018 г. 3:21

Честно говоря... не знаю... Я не работаю с Visual Studio вообще. В основном Qt Creator, да иногда с CLion. Больше походит на то, что у вас некорректно установлены пути к библиотекам Qt в операционной системе. Либо сама Visual Studio криво встала.

Evgenii Legotckoi
  • 13 февраля 2018 г. 3:56

По code-style

Признаюсь честно, сам находился в плавающем состоянии, когда писал ранние статьи, как раз чтобы всё уложить в голове по полочкам, поэтому многие ваши замечания весьма к месту. Но сейчас могу более адекватно и развёрнуто Вам ответить на ваши вопросы.
  1. override в Qt используется, но они в своей странной манере в очередной раз всё переопределили своими макросами, хотя наверняка были ещё какие-то причины, которые сейчас можно считать морально устаревшими. По факту у них есть макрос Q_OVERRIDE, но я всегда пишу override (до кучи ещё и virtual добавляю), а в использовании Q_OVERRIDE не вижу смысла.
  2. На момент изучения всей этой кухни Qt не особо обратил внимание на этот момент, да и не работал плотно в рамках стандарта C++11, а так да, лучше использовать nullptr . Сейчас всегда его использую.
  3. Это уже вопрос вкусовщины. Я придерживался рамок code-style Qt, а по факту можно и не писать имя переменной, разницы принципиально никакой не будет. Q_UNUSED всё равно приводит переменную к void и типо она используется.
  4. Относительно конструкторов и деструкторов - это привычка, писать их реализацию.
  5. Там лучше передать parent`а в конструктор базового класса, мой косяк.
  6. Насчёт памяти в Qt такая ситуация, у всех классов, наследованных от Qt желательно передавать parent. Тогда при уничтожении, parent будет прибирать за собой всех потомков, поэтому и не потребуется использовать умные указатели. В этом примере некоторые недочёты имеются, нужно передавать парента при создании объекта, чего я не везде сделал, тогда утечек не будет.
  7. Лучше, как вы и сказали объявлять объекты членами класса, там например QTimer можно объявить  на стеке. Не помню, только с графической сценой как лучше будет, точно помню есть проблемы с объектами QGraphicsItem , их всегда приходится объявлять в куче, на стеке память не выделить. Работать просто не будет. С графической сценой кажется тоже какие-то проблемы в отображении были, не помню уже.
  8. Объявление в качестве slots нужно было для формирования moc файлов, когда использовался старый синтаксис сигналов и слотов на основе макросов. Скомпилироваться-то скомпилируется, если слот не объявлен в секции слотов, но работать не будет... А вот в случае с новым синтаксисом на основе указателей на методы уже да, не обязательно писать slots и т.д. Но по мне здесь лучше оставить их, это уже вопрос код стайла и разделения по секциями методов, которые должны использоваться как слоты, а какие нет.
  9. а вот emit - это действительно пустышки, я пытался найти адекватное объяснение, для чего они, но кроме того, что это синтаксический сахар, который призван указать вызов именно метода сигнала в коде, ничего не нашёл. Обычно всегда пишу их, чтобы глазу было за что зацепиться. Возможно раньше, в более ранних версиях Qt это было обязательным, сейчас точно нет. Но я застал только Qt 4.8, да и то только вскользь.
r
  • 13 февраля 2018 г. 22:35

Спасибо за ответы. Еще хотел спросить. Вы занимались программированием сетевых игр? вот например если у нас 2 клиента (у каждого свой треугольник). Как например синхронизируется картинка, сколько раз в секунду передаются действия? просто не оч понимаю как удается избегать задержек и делать плавную картинку

r
  • 14 февраля 2018 г. 2:24

А и по библиотеке. Зачем у qt своя библиотека вещей которые есть в в stl, boost и тд. Например зачем использовать всякие вектора Qt и тд когда они есть в stl? Или это зачем то нужно?

Evgenii Legotckoi
  • 14 февраля 2018 г. 2:38

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


По факту синхронизация изображения должна соответствовать как минимум FPS, в идеале не ниже 60 раз в секунду, лучше больше. Но для очень динамичных игр используется не только передача данных о положении объектов, но и методологии предсказания будущего положения игроков и объектов в самой игре без получения данных от сервера или другого игрока. Именно поэтому иногда возникают лаги, когда вашего игрока перекидывает в другое место карты. Просто предсказание сработало неправильно, а при очередном обновлении данных от сервера игра скорректировала положения игрока на карте.  Это очень большая и обширная тема... к сожалению я тут профан.

Моё основное направление сейчас - это программное обеспечение для расчётов нагрузок строительных конструкций и зданий. В основном модели данных и взаимодействие всех компонентов системы. И т.д.
Evgenii Legotckoi
  • 14 февраля 2018 г. 2:48

Ммм... здесь за версту отдаёт желанием написать всё своё. Кстати, голос разума у них всё-таки проснулся в Qt 5. Например, для сортировки там используется и рекомендуется уже std::sort , вместо qSort, который они пометили как deprecated. Что касается контейнеров, то у контейнеров Qt есть кое-какие удобные методы. Но последнее время я перехожу на контейнеры stl. Они гораздо функциональнее. Что касается boost, то сигналы и слоты, которые есть в boost, как говорят в интернетах, слизали именно с Qt. Так что все понемножку друг у друга заимствуют.



r
  • 14 февраля 2018 г. 23:35

я переопределил paint треугольника. сделал рисование танчика и его пушки.

painter->drawPixmap(-30, -50, *spriteImage, 40, 0, 60, 100);
painter->drawPixmap(-20, -50, *spriteImage, 0, 0, 40, 100);
но текстура пушки белым фоном закрашивает часть танка (см скрины https://yadi.sk/d/QKmAHw8e3SL5A4 ). Как это исправить?
r
  • 14 февраля 2018 г. 23:57

разобрался. заменил белый фон на альфаканал.  теперь другой вопрос. вот  Pixmap пушки я хочу поворачивать вокруг своей оси. можно это сделать както через setRotation например? (тоесть нарисовал корпус, повернул обьект, нарисовал пушку)

r
  • 15 февраля 2018 г. 1:32

Фух и с этим разобрался. Теперь такой вопрос, а можно ли как-то по лучше обрабатывать столкновения обьектов, и чтоб к стене например можно было подъехать в плотную, и при этом не вьехать в ней повернувшись на месте?

Evgenii Legotckoi
  • 15 февраля 2018 г. 3:28

Здесь уже нужно обрабатывать очертания танка и не давать возможность ему разворачиваться, если есть риск въехать в стену.

Но для этого нужно немного изменить логику обработки коллизий. Дело в том, что в данном варианте коллизии обрабатываются по факту, когда коллизии уже произошли.
А вот следующим шагом можете начать обрабатывать коллизии с предсказанием. То есть условно взять будущее положение танка и проверить на коллизии. У графической сцены есть метод items(), в этот метод можно передать объект QPolygonF, который можете описывать очертания танка в его будущем положении на карте. Если будет возвращён хотя бы один объект, то моно считать, что коллизия имеет место быть, а значит танк нельзя подвинуть или повернуть, а тогда просто не выполняете движение танка в заданном положении.
r
  • 16 февраля 2018 г. 22:22

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

Evgenii Legotckoi
  • 17 февраля 2018 г. 2:46

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


Нечто подобное я сделал вот в этой статье . Посмотрите следующий метод void Spider::slotGameTimer().

И ещё, у меня просьба. Создавайте, пожалуйста, новые вопросы на форуме . Там можно при создании вопроса указать статью, к которой имеет отношение данный вопрос. Так будет лучше для развития ресурса. Спасибо.
a
  • 7 мая 2018 г. 11:45

forgetting otnasledovat class from QGraphicsScene :D

Evgenii Legotckoi
  • 7 мая 2018 г. 16:09

Ну да, поленился внимательно посмотреть перевод после Google Translate :D

Спасибо, поправил
P.
  • 11 июля 2019 г. 14:08

Добрый день!
Я хотел объединить 2 ваших уроков: "GameDev на Qt" и "Как написать игру на Qt", а именно:
1) Передвижение персонажа и его оружия;
2) Вращение оружия с помощью мыши.
Но чтобы я не делал, у меня уже на постоянной основе 10 внутренних, непонятных мне, ошибок.
Если есть возможность, не могли бы вы мне подсказать, что я делаю не так и как это можно это исправить.
Для удобства скидываю всю программу и скриншот ошибок.

NoNameGames.rar NoNameGames.rar

Evgenii Legotckoi
  • 11 июля 2019 г. 14:13
  • (ред.)

Добрый день! На сам проект у меня пока нет времени посмотреть, но судя по ошибке, вам нужно добавить макрос Q_OBJECT в triangle и в widget.

Обычно выглядит так

  1. class Triangle
  2. {
  3. Q_OBJECT
  4. public:
  5. // остальной код
  6. };
  7.  
P.
  • 11 июля 2019 г. 14:22

В том то и дело, я добавлял, удалял, миксовал, но всё равно эти ошибки появляются раз за разом.
Если во всех файлах оставить "Q_OBJECT", то появляются данные ошибки:

G
  • 10 декабря 2021 г. 22:22

Здравствуйте! Прохожу 1ю часть GameDev на Qt. Сделал код, однако выдает ошибки. Не подскажете что с этим делать?

ЛД
  • 14 июля 2022 г. 0:04
  • (ред.)

Вполне возможно, что ты не закинул graphicsView в дизайнере в виджет

ЛД
  • 14 июля 2022 г. 0:07

Кому интересно, поворот в slotTarget можно в одну строку организовать

  1. this->setRotation(90 + rotation() + qRadiansToDegrees(qAtan2(mapFromScene(point).y(), mapFromScene(point).x())));
ЛД
  • 14 июля 2022 г. 0:10

Вполне возможно, что ты не закинул graphicsView в дизайнере в виджет

Комментарии

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