Evgenii Legotckoi
Evgenii LegotckoiҚаз. 1, 2015, 10:55 Т.Ж.

GameDev on Qt - 2-сабақ. Qt-де түсіру үшін оқ сыныбын жазу

Біз кейіпкерімізді басқара бастағаннан кейін және оның көзі үнемі нысанаға бұрылғаннан кейін, оқтар мен олардың ойын сахнасы арқылы ұшуына жауап беретін Оқ сыныбын жазу уақыты келді. Графикалық көріністе оқты жылжыту механикасы басты кейіпкерді жылжыту механикасына ұқсас болады. Айырмашылығы мынада болады: оқ әрқашан түзу сызықта қозғалады және оқтың ұшу бағытын орнату үшін Bullet, сынып объектісін жасау кезінде ғана оқтың бұрылысын орнату қажет болады.

Bullet класы бар жоба құрылымы

Алдыңғы оқулықтан жобаның құрылымы жаңа класс Bullet қосылған деген мағынада өзгертілген. Барлық басқа сыныптар да қажет болады жаңа сыныппен өзара әрекеттесуін қамтамасыз ету үшін аяқталады.

Сондықтан, ретімен бастайық, атап айтқанда, ату процесін тудыратын оқиға инициализацияланған сыныптан.


customscene.h

Теңшелетін графикалық көріністе тінтуірді шертуді өңдеуге арналған функцияларды ( mousePressEvent және mouseReleaseEvent ), сондай-ақ графикалық көріністен басты кейіпкерге түсіруге рұқсат беру сигналын қосу қажет болады. Әрине, басты кейіпкерлер класында тінтуірдің шертулерін де тексеруге болады, бірақ мәселе мынада: тек графикалық көрініс аймағында түсіруге рұқсат беру керек, өйткені онда суретке түсіру оқиғаларын тудырмайтын басқа интерактивті элементтер болуы мүмкін.

#ifndef CUSTOMSCENE\_H
#define CUSTOMSCENE\_H

#include <QObject>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>

class CustomScene : public QGraphicsScene
{
    Q\_OBJECT
public:
    explicit CustomScene(QObject *parent = 0);
    ~CustomScene();

signals:
    // Сигнал для передачи координат положения курсора мыши
    void signalTargetCoordinate(QPointF point);
    void signalShot(bool shot); // Сигнал на стрельбу

public slots:

private:
    // Функция, в которой производится отслеживание положения мыши
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};

#endif // CUSTOMSCENE\_H

customscene.cpp

#include "customscene.h"

CustomScene::CustomScene(QObject *parent) :
    QGraphicsScene()
{
    Q\_UNUSED(parent);
}

CustomScene::~CustomScene()
{

}

void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    emit signalTargetCoordinate(event->scenePos());
}

void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    emit signalShot(true); // Когда клавиша мыши нажата, то можно стрелять
    Q\_UNUSED(event);
}

void CustomScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    emit signalShot(false); // Когда клавишу мыши отпустили, то стрелять нельзя
    Q\_UNUSED(event);
}

виджет.h

Түсіру ажыратымдылығы туралы деректерді тасымалдау үшін графикалық көріністен signalShot құралын үшбұрыштағы slotShot -ға қосамыз. Ол графикалық көрініс аймағында тінтуірдің түймесі басылғаны туралы ақпаратты жібереді. Сондай-ақ, маркер жасауды бастайтын signalBullet үшбұрыштан ойын өзегіне, Виджет сыныбына беріледі. Bullet сыныбының нысаны slotBullet ішінде жасалады. слот және графикалық сахнада орнатылған.

#ifndef WIDGET\_H
#define WIDGET\_H

#include <QWidget>
#include <QGraphicsScene>
#include <QGraphicsItem>

#include <triangle.h>
#include <customscene.h>
#include <bullet.h>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q\_OBJECT

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

private:
    Ui::Widget *ui;
    CustomScene  *scene;    // Объявляем графическую сцену
    Triangle *triangle;     // Объявляем треугольник

private slots:
    void slotBullet(QPointF start, QPointF end);
};

#endif // WIDGET\_H

widget.cpp

#include "widget.h"
#include "ui\_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    /* Программный код из урока
     * GameDev. Отслеживание перемещения мыши в QGraphicsScene
     */
    // Соединяем сигнала стрельбы с графической сцены со слотом разрешения стрельбы треугольника
    connect(scene, &CustomScene::signalShot, triangle, &Triangle::slotShot);
    // Соединяем сигнал на создание пули со слотом, создающим пули в игре
    connect(triangle, &Triangle::signalBullet, this, &Widget::slotBullet);
}

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

void Widget::slotBullet(QPointF start, QPointF end)
{
    // Добавляем на сцену пулю
    scene->addItem(new Bullet(start, end));
}

үшбұрыш.сағ

Басты кейіпкердің сыныбында графикалық көріністен атуға рұқсат беру үшін сигнал алынса, секундына 6 рет жаңа оқ құруды бастайтын жаңа таймер жасау керек. Атуды тоқтату сигналы түскен бойда оқтардың жасалуы еленбейді.

#ifndef TRIANGLE\_H
#define TRIANGLE\_H

#include <QObject>
#include <QGraphicsItem>
#include <QPainter>
#include <QPolygon>
#include <QTimer>
#include <QDebug>
#include <QCursor>

#include <windows.h>

class Triangle : public QObject, public QGraphicsItem
{
    Q\_OBJECT
public:
    explicit Triangle(QObject *parent = 0);
    ~Triangle();

signals:
    // Сигнал для создания пули с параметрами траектории
    void signalBullet(QPointF start, QPointF end);

public slots:
    // Слот для получения данных о положении курсора
    void slotTarget(QPointF point);
    // слот для обработки разрешения стрельбы
    void slotShot(bool shot);

private:
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

private slots:
    void slotGameTimer();   // Игровой слот
    void slotBulletTimer(); // Слот проверки пули

private:
    bool shot;              // Переменная состояния стрельбы
    QTimer *bulletTimer;    // Таймер пули
    QTimer *gameTimer;      // Игровой таймер
    QPointF target;         // Положение курсора
};

#endif // TRIANGLE\_H

triangle.cpp

#include "triangle.h"
#include <math.h>

static const double Pi = 3.14159265358979323846264338327950288419717;
static double TwoPi = 2.0 * Pi;

static qreal normalizeAngle(qreal angle)
{
    while (angle < 0)
        angle += TwoPi;
    while (angle > TwoPi)
        angle -= TwoPi;
    return angle;
}

Triangle::Triangle(QObject *parent) :
    QObject(parent), QGraphicsItem()
{
    setRotation(0);      // Устанавливаем исходный разворот треугольника

    target = QPointF(0,0);  // Устанавливаем изначальное положение курсора
    shot = false;

    gameTimer = new QTimer();   // Инициализируем игровой таймер
    // Подключаем сигнал от таймера и слоту обработки игрового таймера
    connect(gameTimer, &QTimer::timeout, this, &Triangle::slotGameTimer);
    gameTimer->start(10);   // Стартуем таймер

    bulletTimer = new QTimer(); // Инициализируем таймер создания пуль
    connect(bulletTimer, &QTimer::timeout, this, &Triangle::slotBulletTimer);
    bulletTimer->start(1000/6); // Стреляем 6 раз в секунду


}

Triangle::~Triangle()
{

}

QRectF Triangle::boundingRect() const
{
    return QRectF(-20,-30,40,60);
}

void Triangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    /** Отрисовка треугольника
     * */
    QPolygon polygon;
    polygon << QPoint(0,-30) << QPoint(20,30) << QPoint(-20,30);
    painter->setBrush(Qt::red);
    painter->drawPolygon(polygon);

    Q\_UNUSED(option);
    Q\_UNUSED(widget);
}

void Triangle::slotTarget(QPointF point)
{
    /* Программный код из урока
     * GameDev. Отслеживание перемещения мыши в QGraphicsScene
     */
}

void Triangle::slotGameTimer()
{
   /* Программный код из урока
    * GameDev. Отслеживание перемещения мыши в QGraphicsScene
    */
}

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

}

void Triangle::slotShot(bool shot)
{
    this->shot = shot;  // Получаем разрешение или запрет на стрельбу
}

bullet.h

Енді Bullet класын пайдаланып маркердің өзін жасауды бастайық. Бұл класс QGraphicsItem ішінен мұраланған және онымен жұмыс істеу басты кейіпкермен жұмыс істеуге ұқсас. Оқ ойын аймағынан шыққанда, бағдарлама жадын босату үшін оны жою керек.

#ifndef BULLET\_H
#define BULLET\_H

#include <QObject>
#include <QGraphicsItem>
#include <QTimer>
#include <QPainter>

class Bullet : public QObject, public QGraphicsItem
{
    Q\_OBJECT
public:
    explicit Bullet(QPointF start, QPointF end, QObject *parent = 0);
    ~Bullet();

signals:


public slots:

private:
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

private:
    QTimer *timerBullet;    // Слот для обработки таймера пули

private slots:
    void slotTimerBullet(); // Слот для обработки полёта пули
};

#endif // BULLET\_H

bullet.cpp

Маркер конструкторына оқтың ұшу жолын анықтайтын екі QPointF, нысаны беріледі. Бірінші координат – оқтың шығу орны, оны жасау басталған орын, ал екінші координат – түсіру кезіндегі курсордың орны. Осы екі координатқа сәйкес оның ұшу траекториясы белгіленеді.

Кенелердегі ойын оқ таймері ойын ұяшығының іске қосылуын бастайды, онда оқ 10 пиксель алға жылжиды және ойын аймағынан кетсе, жойылады.

#include "bullet.h"
#include <math.h>

static const double Pi = 3.14159265358979323846264338327950288419717;
static double TwoPi = 2.0 * Pi;

static qreal normalizeAngle(qreal angle)
{
    while (angle < 0)
        angle += TwoPi;
    while (angle > TwoPi)
        angle -= TwoPi;
    return angle;
}

Bullet::Bullet(QPointF start, QPointF end, QObject *parent)
    : QObject(parent), QGraphicsItem()
{
    this->setRotation(0);   // Устанавливаем начальный разворот
    this->setPos(start);    // Устанавливаем стартовую позицию
    // Определяем траекторию полёта пули
    QLineF lineToTarget(start, end);
    // Угол поворота в направлении к цели
    qreal angleToTarget = ::acos(lineToTarget.dx() / lineToTarget.length());
    if (lineToTarget.dy() < 0)
        angleToTarget = TwoPi - angleToTarget;
    angleToTarget = normalizeAngle((Pi - angleToTarget) + Pi / 2);

    /* Разворачиваем пули по траектории
     * */
    if (angleToTarget >= 0 && angleToTarget < Pi) {
        /// Rotate left
        setRotation(rotation() - angleToTarget * 180 /Pi);
    } else if (angleToTarget <= TwoPi && angleToTarget > Pi) {
        /// Rotate right
        setRotation(rotation() + (angleToTarget - TwoPi )* (-180) /Pi);
    }
    // Инициализируем полётный таймер
    timerBullet = new QTimer();
    // И подключаем его к слоту для обработки полёта пули
    connect(timerBullet, &QTimer::timeout, this, &Bullet::slotTimerBullet);
    timerBullet->start(7);
}

Bullet::~Bullet()
{

}

QRectF Bullet::boundingRect() const
{
    return QRectF(0,0,2,4);
}

void Bullet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setPen(Qt::black);
    painter->setBrush(Qt::black);
    painter->drawRect(0,0,2,4);

    Q\_UNUSED(option);
    Q\_UNUSED(widget);
}

void Bullet::slotTimerBullet()
{
    setPos(mapToParent(0, -10));

    /* Проверка выхода за границы поля
     * Если пуля вылетает за заданные границы, то пулю необходимо уничтожить
     * */
    if(this->x() < 0){
        this->deleteLater();
    }
    if(this->x() > 500){
        this->deleteLater();
    }

    if(this->y() < 0){
        this->deleteLater();
    }
    if(this->y() > 500){
        this->deleteLater();
    }
}

Барлығы

Нәтижесінде басты кейіпкеріміз ату қабілетіне ие болды. Бұл мақаланың бейне оқулығында егжей-тегжейлі көрсетілген. Бейне оқулық сонымен қатар бағдарлама кодына түсініктеме береді.

Бейне оқулық

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

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

k
  • Ақп. 6, 2018, 1:41 Т.Қ.

почему-то при компиляции ругается на Bullet тут

  1. void Widget::slotBullet(QPointF start, QPointF end)
  2. {
  3. // Добавляем на сцену пулю
  4. scene->addItem(new Bullet(start, end));
  5. }
ошибка: invalid new-expression of abstract class type 'Bullet'
Почему компилятор считает его абстрактным?
Qt 5.10, компилятор minGW 5.3.0
Evgenii Legotckoi
  • Ақп. 6, 2018, 5:19 Т.Қ.

Вы переопределили методы paint и boundingRect?

И возможно, что требуется переопределить ещё какие-то методы в Qt 5.10. Посмотрите, что там есть из виртуальных методов у базового класса bullet.
Конкретно посмотрите методы которые приравнены нулю, например, с такой записью
virtual void someMethod() = 0;
k
  • Ақп. 12, 2018, 11:33 Т.Ж.

Все оказалось куда проще: не поставил const перед QStyleOptionGraphicsItem *option как в заголовочном файле, так и в фале исходного кода.

Зато получше познакомился с интерфейсом Qt Creator, пока следовал вашему совету, очень удобный. Спасибо :)
Evgenii Legotckoi
  • Ақп. 12, 2018, 4:17 Т.Қ.

Наиболее удобный интерфейс и управление у CLion, однако он не имеет поддержки синтаксиса и макросов Qt, что очень сильно тормозит скорость разработки, по сравнению с Qt Creator, а также абсолютно нет поддержки QML, кроме синтаксиса с помощью плагина.

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
OI
  • Ora Iro
  • Жел. 24, 2024, 6:38 Т.Ж.

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:40ұпай,
  • Бағалау ұпайлары-8
AD

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

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

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

  • Нәтиже:80ұпай,
  • Бағалау ұпайлары4
Соңғы пікірлер
ИМ
Игорь МаксимовҚар. 22, 2024, 11:51 Т.Ж.
Django - Оқулық 017. Теңшелген Django кіру беті Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiҚаз. 31, 2024, 2:37 Т.Қ.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEҚаз. 19, 2024, 8:19 Т.Ж.
Qt Creator көмегімен fb3 файл оқу құралы Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовҚаз. 5, 2024, 7:51 Т.Ж.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Қар. 15, 2024, 6:04 Т.Ж.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectМаусым 4, 2022, 3:49 Т.Ж.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimҚаз. 25, 2024, 9:10 Т.Ж.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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