Реклама

GameDev на Qt - Урок 4. Обнаружение коллизий в Qt (2D)

РуководствоQtGameDev, Qt, Collision, Коллизия1828

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

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

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

Если вы пишите платформер на Qt, то для объектов QGraphicsItem имеется возможность использовать функцию графической сцены, в которой они находятся, а именно QGraphicsScene ::collidingItems() . В данную функцию передаётся в качестве аргумента тот объект, для которого Мы хотим произвести обнаружение коллизий. Функция возвращает список графических объектов, если смогла их обнаружить в области шара для хомяка нашего героя. И если Мы обнаруживаем, что герой наткнулся при очередном шаге на какой-то объект, то Мы заставляем героя тут же сделать шаг назад.

Реализуем обнаружение коллизий

А теперь приступим к реализации разрешения коллизий на основе программного кода из предыдущих уроков по GameDev в Qt:

widget.cpp

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

    scene->addRect(0,0,520,20,QPen(Qt::NoPen),QBrush(Qt::darkGray));
    scene->addRect(0,0,20,520,QPen(Qt::NoPen),QBrush(Qt::darkGray));
    scene->addRect(0,500,520,20,QPen(Qt::NoPen),QBrush(Qt::darkGray));
    scene->addRect(500,0,20,520,QPen(Qt::NoPen),QBrush(Qt::darkGray));
    scene->addRect(170,250,180,20,QPen(Qt::NoPen),QBrush(Qt::darkGray));
    scene->addRect(250,170,20,180,QPen(Qt::NoPen),QBrush(Qt::darkGray));

triangle.h

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

private:
    QRectF boundingRect() const;

triangle.cpp

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

// Изменены размеры героя
QRectF Triangle::boundingRect() const
{
    return QRectF(-12,-15,24,30);
}

/* Возвращаем форму героя
 * В данном случае лучше использовать эллипс
 * */
QPainterPath Triangle::shape() const
{
    QPainterPath path;
    path.addEllipse(boundingRect());
    return path;
}

void Triangle::slotBulletTimer()
{
    /// Если стрельба разрешена, то вызываем сигнал на создание пули
    if(shot) emit signalBullet(mapToScene(0,-25), target);

}

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

void Triangle::slotGameTimer()
{
    /** Перемещаем треугольник в зависимости от нажатых кнопок
     * */
    if(GetAsyncKeyState('A')){
        this->setX(this->x() - 1);
        /* Проверяем на столкновение,
         * если столкновение произошло,
         * то возвращаем героя обратно в исходную точку
         * */
        if(!scene()->collidingItems(this).isEmpty()){
            this->setX(this->x() + 1);
        }
    }
    if(GetAsyncKeyState('D')){
        this->setX(this->x() + 1);
        /* Проверяем на столкновение,
         * если столкновение произошло,
         * то возвращаем героя обратно в исходную точку
         * */
        if(!scene()->collidingItems(this).isEmpty()){
            this->setX(this->x() - 1);
        }
    }
    if(GetAsyncKeyState('W')){
        this->setY(this->y() - 1);
        /* Проверяем на столкновение,
         * если столкновение произошло,
         * то возвращаем героя обратно в исходную точку
         * */
        if(!scene()->collidingItems(this).isEmpty()){
            this->setY(this->y() + 1);
        }
    }
    if(GetAsyncKeyState('S')){
        this->setY(this->y() + 1);
        /* Проверяем на столкновение,
         * если столкновение произошло,
         * то возвращаем героя обратно в исходную точку
         * */
        if(!scene()->collidingItems(this).isEmpty()){
            this->setY(this->y() - 1);
        }
    }

    // Код из предыдущих уроков
}

Итог

В результате главный герой игры уже не сможет ходить сквозь мишени, а также сквозь стены. Чтобы прорваться сквозь строй мишеней, ему придётся их расстрелять. Демонстрация работы приложения приведена в видеоуроке.

Видеоурок

Реклама

Комментарии

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

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

Спасибо за ответ. Думаю многие ищут эту информацию. Особенно если рассмотрите вопрос статической линковки и сторонних библиотек. Ну конечно желательно и как будет возможность.


Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
Последние комментарии
  • EVILEG
  • 13 июля 2017 г. 2:12

Qt/C++ - Урок 023. Перетаскивание QGraphicsItem на QGraphicsScene мышью

Ну например так можете сделать.void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event){ if (QApplication::mouseButtons() == Qt::RightButton) { this->deleteLa...

  • Mark
  • 13 июля 2017 г. 1:26

Qt/C++ - Урок 023. Перетаскивание QGraphicsItem на QGraphicsScene мышью

Подскажите пожалуйста как в данном проекте по перетаскиванию организовать удаление объекта со scene методом delete item, допустим при щелчке ПКМ по объекту QGraphicsScene. Мои попытки оказалис...

  • EVILEG
  • 10 июля 2017 г. 21:34

Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread

А что делали? Повторяете урок или как? Пытались просто скачать проект в конце статьи и запустить?

Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread

У меня происходит переполнение счетчика count, появляется ошибка malloc(): memory corruption (fast). Не подскажите, как с этим бороться?

  • EVILEG
  • 9 июля 2017 г. 2:07

GameDev на Qt - Урок 3. Уничтожение противников

Поэтому в пятом уроке есть исходники всего проекта )))). Вообще, все эти материалы были не предыдущей версии сайта, которая на WordPress. Во время переноса мог что-то потерять.

Сейчас обсуждают на форуме

тестирование классов в QT

это вопрос.

Как реализовать отправку e-mail

Возможно что уже и нет необходимости в почтовом клиенте, но в своё время так же столкнулся с данной проблемой в QT. Нашел один интересный проект под названием libqxt, там реализовано дов...

  • Asteri
  • 14 июля 2017 г. 12:23

css

Делюсь, может, пригодится когда-нибудь) QTableView QHeaderView { background-color: #ffffff; } Вот так эта проблема лечится, градиент задать не получается, но хоть...

  • EVILEG
  • 12 июля 2017 г. 19:52

QSqlQuery выполнение sql запросов из файла

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

QML Canvas + Line. Bug?

Вот оно что, значит не баг) Спасибо