From c58ade6eeb7cc191eb7d34ade7e0a0f7e29e0091 Mon Sep 17 00:00:00 2001 From: Alina <99394498+alepopas@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:53:10 +0300 Subject: [PATCH 1/2] :( --- Pacman.pro | 60 +++++++----- README.md | 52 +++++------ dragitem.cpp | 34 +++++++ dragitem.h | 30 ++++++ itemsfactory.cpp | 48 ++++++++++ itemsfactory.h | 24 +++++ itemswidget.cpp | 57 ++++++++++++ itemswidget.h | 25 +++++ main.cpp | 22 ++--- mainwindow.cpp | 146 ++++++++++++++++++++++++++--- mainwindow.h | 66 ++++++++----- mainwindow.ui | 237 ++++++++++++++++++++++++++++++++++++++++++----- pacmangame.cpp | 131 ++++++++++++++++++++++++++ pacmangame.h | 47 ++++++++++ pacmanitem.cpp | 78 ++++++++++++++++ pacmanitem.h | 42 +++++++++ scene.cpp | 123 ++++++++++++++++++++++++ scene.h | 38 ++++++++ 18 files changed, 1141 insertions(+), 119 deletions(-) create mode 100644 dragitem.cpp create mode 100644 dragitem.h create mode 100644 itemsfactory.cpp create mode 100644 itemsfactory.h create mode 100644 itemswidget.cpp create mode 100644 itemswidget.h create mode 100644 pacmangame.cpp create mode 100644 pacmangame.h create mode 100644 pacmanitem.cpp create mode 100644 pacmanitem.h create mode 100644 scene.cpp create mode 100644 scene.h diff --git a/Pacman.pro b/Pacman.pro index b915c09..c191ad7 100644 --- a/Pacman.pro +++ b/Pacman.pro @@ -1,24 +1,36 @@ -QT += core gui - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -CONFIG += c++17 - -# You can make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -SOURCES += \ - main.cpp \ - mainwindow.cpp - -HEADERS += \ - mainwindow.h - -FORMS += \ - mainwindow.ui - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++17 + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + dragitem.cpp \ + itemsfactory.cpp \ + itemswidget.cpp \ + main.cpp \ + mainwindow.cpp \ + pacmangame.cpp \ + pacmanitem.cpp \ + scene.cpp + +HEADERS += \ + dragitem.h \ + itemsfactory.h \ + itemswidget.h \ + mainwindow.h \ + pacmangame.h \ + pacmanitem.h \ + scene.h + +FORMS += \ + mainwindow.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/README.md b/README.md index fbc794e..d1b713b 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,26 @@ -# Игра Pac-Man -## Pac-Man -- Сделать реализацию движения (4 стороны) на кнопки. При нажатии меняется только направление. Пакман движется сам. -- Пакман может быть абсолютно любой формы, кроме прямоугольника и квадрата, главное правильно переопределить `boundingRect` и `shape` для сложных фигур -## Сцена -- Перед началом игры должна быть возможность создать уровень самому, перетащив все стены, монетки и пакмана с помощью drag and drop механизма. -- Пакман должен быть только один, тогда как монеток и стен можно разместить сколько угодно или какое-то заданное число, не меньше 10. -## Стены -- Стены могут быть просто прямоугольниками, которые можно вращать на сцене, чтобы создавать вертикальные и горизонтальные -## Монетки -- При соприкосновении с пакманом монетка исчезает со сцены -- Форма – любая отличная от прямоугольника и квадрата. -## Минимальный интерфейс -- Представление графической сцены (`QGraphicsView`) -- Набор фигур для Drag and Drop -- Кнопка “Начать игру” -- Кнопка “Стоп” -- Меню “Справка” с описанием горячих клавиш и особыми правилами (если такие имеются). -- Счетчик собранных момент -## Геймплей -- Если пакман сталкивается со стеной, то до момента пока он не отойдет от нее, его цвет должен меняться на другой. -- Монетки при сборе должны исчезать со сцены и добавляться в счетчик. -- Игра оканчивается при сборе всех монеток или по кнопке “Стоп”. -- Если собраны все монетки, выводится поздравительное сообщение. -- Когда игра началась, нельзя добавить новые фигуры или менять положение текущих (кроме перемещения самого пакмана). -- Все столкновения на сцене обрабатываются с помощью методов `QGraphicsScene::collidingItems`, `QGraphicsItem::collidingItems` и т.д. +# Игра Pac-Man +## Pac-Man +- Сделать реализацию движения (4 стороны) на кнопки. При нажатии меняется только направление. Пакман движется сам. +- Пакман может быть абсолютно любой формы, кроме прямоугольника и квадрата, главное правильно переопределить `boundingRect` и `shape` для сложных фигур +## Сцена +- Перед началом игры должна быть возможность создать уровень самому, перетащив все стены, монетки и пакмана с помощью drag and drop механизма. +- Пакман должен быть только один, тогда как монеток и стен можно разместить сколько угодно или какое-то заданное число, не меньше 10. +## Стены +- Стены могут быть просто прямоугольниками, которые можно вращать на сцене, чтобы создавать вертикальные и горизонтальные +## Монетки +- При соприкосновении с пакманом монетка исчезает со сцены +- Форма – любая отличная от прямоугольника и квадрата. +## Минимальный интерфейс +- Представление графической сцены (`QGraphicsView`) +- Набор фигур для Drag and Drop +- Кнопка “Начать игру” +- Кнопка “Стоп” +- Меню “Справка” с описанием горячих клавиш и особыми правилами (если такие имеются). +- Счетчик собранных момент +## Геймплей +- Если пакман сталкивается со стеной, то до момента пока он не отойдет от нее, его цвет должен меняться на другой. +- Монетки при сборе должны исчезать со сцены и добавляться в счетчик. +- Игра оканчивается при сборе всех монеток или по кнопке “Стоп”. +- Если собраны все монетки, выводится поздравительное сообщение. +- Когда игра началась, нельзя добавить новые фигуры или менять положение текущих (кроме перемещения самого пакмана). +- Все столкновения на сцене обрабатываются с помощью методов `QGraphicsScene::collidingItems`, `QGraphicsItem::collidingItems` и т.д. diff --git a/dragitem.cpp b/dragitem.cpp new file mode 100644 index 0000000..0b30887 --- /dev/null +++ b/dragitem.cpp @@ -0,0 +1,34 @@ +#include "dragitem.h" + +#include +#include + +DragItem::DragItem(ItemsFactory::ItemsType type, QWidget *parent) + : QWidget{parent}, QListWidgetItem() { + graphicsItem_ = ItemsFactory::Create(type); + type_ = type; + + QRect geometry = graphicsItem_->boundingRect().toRect(); + setGeometry(geometry); + + // Задаем размер элемента списка (QListWidgetItem) + // Делаем его чуть больше в размере, чтобы элементы списка не слипались + int additionalHeight = 2; + setSizeHint(QSize(geometry.width(), geometry.height() + additionalHeight)); +} + +// Отрисовываем нашу фигуру в списке +void DragItem::paintEvent(QPaintEvent *event) { + Q_UNUSED(event) + + QPainter painter(this); + painter.setPen(graphicsItem_->pen()); + painter.setBrush(graphicsItem_->brush()); + + QStyleOptionGraphicsItem itemOption; + graphicsItem_->paint(&painter, &itemOption, nullptr); +} + +PacmanItem *DragItem::GetGraphicsItem() const { return graphicsItem_; } + +ItemsFactory::ItemsType DragItem::GetType() const { return type_; } diff --git a/dragitem.h b/dragitem.h new file mode 100644 index 0000000..f59d319 --- /dev/null +++ b/dragitem.h @@ -0,0 +1,30 @@ +#ifndef DRAGITEM_H +#define DRAGITEM_H + +#include + +#include +#include +#include + +class DragItem : public QWidget, public QListWidgetItem { + Q_OBJECT + public: + explicit DragItem(ItemsFactory::ItemsType type, QWidget *parent = nullptr); + + protected: + void paintEvent(QPaintEvent *event); + + public: + PacmanItem *GetGraphicsItem() const; + ItemsFactory::ItemsType GetType() const; + + public: + static const inline QString kMimeFormat = "application/x-dnditemdata"; + + private: + PacmanItem *graphicsItem_; + ItemsFactory::ItemsType type_; +}; + +#endif // DRAGITEM_H diff --git a/itemsfactory.cpp b/itemsfactory.cpp new file mode 100644 index 0000000..df8b433 --- /dev/null +++ b/itemsfactory.cpp @@ -0,0 +1,48 @@ +#include "itemsfactory.h" + +ItemsFactory::ItemsFactory() {} + +// Крайне примитивная реализация фабричного метода для выделения одного места, +// ответственного за создание фигур +PacmanItem *ItemsFactory::Create(ItemsType type) { + QPainterPath figurePath; + + QPen pen(Qt::black, 1); + QBrush brush(GetColorByItemType(type), Qt::SolidPattern); + + + switch (type) { + case ItemsType::kWall: + figurePath.addRect(0, 0, 70, 20); + break; + + case ItemsType::kCoin: + figurePath.addEllipse(0, 0, 20, 20); + break; + + case ItemsType::kPlayer: + figurePath.addEllipse(0, 0, 40, 40); + break; + + default: + figurePath.addRect(0, 0, 70, 20); + } + + return new PacmanItem(figurePath, pen, brush); +} + +QColor ItemsFactory::GetColorByItemType(ItemsType type) { + switch (type) { + case ItemsType::kWall: + return Qt::gray; + + case ItemsType::kPlayer: + return Qt::red; + + case ItemsType::kCoin: + return Qt::yellow; + + default: + return Qt::black; + } +} diff --git a/itemsfactory.h b/itemsfactory.h new file mode 100644 index 0000000..893373f --- /dev/null +++ b/itemsfactory.h @@ -0,0 +1,24 @@ +#ifndef ITEMSFACTORY_H +#define ITEMSFACTORY_H + +#include +#include +#include + +#include "pacmanitem.h" + +class ItemsFactory : public QObject { + public: + enum ItemsType { kWall = 0, kPlayer = 1, kCoin = 2 }; + Q_ENUM(ItemsType) + + private: + ItemsFactory(); + + public: + static PacmanItem* Create(ItemsType type); + static QColor GetColorByItemType(ItemsType type); +}; + + +#endif // ITEMSFACTORY_H diff --git a/itemswidget.cpp b/itemswidget.cpp new file mode 100644 index 0000000..5ed22cf --- /dev/null +++ b/itemswidget.cpp @@ -0,0 +1,57 @@ +#include "itemswidget.h" + +#include +#include +#include +#include +#include +#include + +ItemsWidget::ItemsWidget(QWidget *parent) : QListWidget(parent) { + setSelectionMode(SingleSelection); + setDragEnabled(true); +} + +void ItemsWidget::mousePressEvent(QMouseEvent *event) { + dragStartPosition_ = event->pos(); + dragableItem_ = dynamic_cast(itemAt(dragStartPosition_)); + + QListWidget::mousePressEvent(event); +} + +void ItemsWidget::mouseMoveEvent(QMouseEvent *event) { + if ((event->buttons() == Qt::LeftButton) == false || + dragableItem_ == nullptr) { + return; + } + + // Записываем в mime тип (спец. тип для переноса данных) + // необходимую для нас информацию - тип фигуры + QByteArray itemData; + QDataStream dataStream(&itemData, QIODevice::WriteOnly); + dataStream << dragableItem_->GetType(); + + QMimeData *mimeData = new QMimeData; + mimeData->setData(dragableItem_->kMimeFormat, itemData); + + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setPixmap(GetPreview()); + + drag->exec(Qt::CopyAction | Qt::MoveAction); +} + +// Отрисовываем перетаскиваемый предмет рядом с мышкой +QPixmap ItemsWidget::GetPreview() const { + auto *item = dragableItem_->GetGraphicsItem(); + + QPixmap pixmap(item->boundingRect().size().toSize()); + pixmap.fill(Qt::transparent); + + QPainter painter(&pixmap); + painter.setPen(item->pen()); + painter.setBrush(item->brush()); + painter.drawPath(item->shape()); + + return pixmap; +} diff --git a/itemswidget.h b/itemswidget.h new file mode 100644 index 0000000..750ee00 --- /dev/null +++ b/itemswidget.h @@ -0,0 +1,25 @@ +#ifndef ITEMSWIDGET_H +#define ITEMSWIDGET_H + +#include + +#include + +class ItemsWidget : public QListWidget { + Q_OBJECT + public: + explicit ItemsWidget(QWidget *parent = nullptr); + + protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + + private: + QPixmap GetPreview() const; + + private: + QPoint dragStartPosition_; + DragItem *dragableItem_; +}; + +#endif // ITEMSWIDGET_H diff --git a/main.cpp b/main.cpp index e9562cc..099ae0d 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,11 @@ -#include "mainwindow.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - MainWindow w; - w.show(); - return a.exec(); -} +#include "mainwindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp index 7e184b2..e2c9c3a 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,15 +1,131 @@ -#include "mainwindow.h" -#include "ui_mainwindow.h" - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::MainWindow) -{ - ui->setupUi(this); -} - -MainWindow::~MainWindow() -{ - delete ui; -} - +#include "mainwindow.h" + +#include +#include + +#include +#include + +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), ui_(new Ui::MainWindow) { + ui_->setupUi(this); + + SetUi(); + SetConnections(); + + ui_->graphicsView->setFocusPolicy(Qt::StrongFocus); + ui_->listWidget->setFocusPolicy(Qt::NoFocus); +} + +MainWindow::~MainWindow() { delete ui_; } + +void MainWindow::AddNewItem() { + // Рисуем эллипс, вписанный в прямоугольник стандартным методом + QGraphicsItem *figure = scene_->addEllipse( + QRectF(0, 0, 70, 40), // Геометрия фигуры + QPen(Qt::black), // Цвет контура фигуры + QBrush(Qt::blue, Qt::SolidPattern)); // Цвет заливки фигуры + + // Включам возможность претаскивать объект по сцене + figure->setFlag(QGraphicsItem::ItemIsMovable, true); + // Включам возможность выделять объект на сцене + figure->setFlag(QGraphicsItem::ItemIsSelectable, true); + + // Эквивалентная запись флагов, установленных выше + figure->setFlags(QGraphicsItem::ItemIsMovable | + QGraphicsItem::ItemIsSelectable); +} + +void MainWindow::AddNewOwnItem(ItemsFactory::ItemsType itemType) { + +} + +void MainWindow::AddNewCoinItem() { + scene_->addItem(ItemsFactory::Create(ItemsFactory::ItemsType::kCoin)); +} + +void MainWindow::UpdateScoreLSD(uint score) { + ui_->Score->display(score); +} + +void MainWindow::keyPressEvent(QKeyEvent *event) { + if (event->key() == Qt::Key_Space) { + auto selectedItems = scene_->selectedItems(); + // Если есть выделенные объекты на сцене, берем первый из них + if (!selectedItems.isEmpty()) { + auto item = selectedItems[0]; + + // Выводим все столкновения объекта item на сцене с помощью метода сцены + qDebug() << "Метод сцены: " << scene_->collidingItems(item) + << Qt::endl + // Для выбранного объекта выводим все столкновения c помощью + // метода объекта + << "Метод объекта: " << item->collidingItems(); + + // Проверяем сталкивается ли объект item с item2 + // item->collidesWithItem(item2) + } + } +} + +void MainWindow::SetConnections() { + + connect(ui_->startButton, &QPushButton::clicked, game_, + &PacmanGame::startNewGame); + connect(ui_->startButton, &QPushButton::clicked, + [=]() { scene_->setFiguresDragAndDropOption(false); }); + + connect(ui_->newItemButon, &QPushButton::clicked, this, + &MainWindow::AddNewItem); + + connect(ui_->newOwnItemButton, &QPushButton::clicked, this, + &MainWindow::AddNewOwnItem); + + connect(ui_->newCoinItemButton, &QPushButton::clicked, this, + &MainWindow::AddNewCoinItem); + + connect(ui_->rotateButton, &QPushButton::clicked, scene_, + &Scene::RotateSelectedItems); + + // Увеличиваем масштаб объектов на itemScaleFactor_ + connect(ui_->zoomInButton, &QPushButton::clicked, this, + [=]() { scene_->ScaleSelectedItems(itemScaleFactor_); }); + + // Уменьшаем масштаб объектов на itemScaleFactor_ + connect(ui_->zoomOutButton, &QPushButton::clicked, this, + [=]() { scene_->ScaleSelectedItems(-itemScaleFactor_); }); + + // Сбрасываем масштаб выделенных объектов + connect(ui_->resetZoomButton, &QPushButton::clicked, scene_, + &Scene::ResetSelectedItemsScale); + + // Удаляем выделенные объекты по нажатию кнопки + connect(ui_->removeButton, &QPushButton::clicked, scene_, + &Scene::RemoveSelectedItems); +} + +void MainWindow::SetUi() { + scene_ = new Scene(); + game_ = new PacmanGame(*scene_, this); + + ui_->graphicsView->setScene(scene_); + ui_->graphicsView->setRenderHint(QPainter::Antialiasing); + + show(); + + QRectF viewRect = ui_->graphicsView->geometry(); + scene_->setSceneRect(viewRect); + + AddNewOwnItem(ItemsFactory::ItemsType::kWall); + AddNewOwnItem(ItemsFactory::ItemsType::kPlayer); + AddNewOwnItem(ItemsFactory::ItemsType::kCoin); +} + +void MainWindow::addItemToList(ItemsFactory::ItemsType itemType) { + DragItem *item = new DragItem(itemType); + ui_->listWidget->addItem(item); + ui_->listWidget->setItemWidget(item, item); +} + diff --git a/mainwindow.h b/mainwindow.h index dbe42ab..6b2d0aa 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -1,21 +1,45 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class MainWindow; } -QT_END_NAMESPACE - -class MainWindow : public QMainWindow -{ - Q_OBJECT - - public: - MainWindow(QWidget *parent = nullptr); - ~MainWindow(); - - private: - Ui::MainWindow *ui; -}; -#endif // MAINWINDOW_H +#ifndef MAINWINDOW_H +#define MAINWINDOW_H +#include +#include + +#include +#include "pacmangame.h" + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow { + Q_OBJECT + + public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + void SetWidgetMoveEnabled(bool enabled); + void addItemToList(ItemsFactory::ItemsType itemType); + + private slots: + void UpdateScoreLSD(uint newScore); + void AddNewItem(); + void AddNewOwnItem(); + void AddNewCoinItem(); + + protected: + void keyPressEvent(QKeyEvent *event); + + private: + void SetConnections(); + void SetUi(); + + private: + Ui::MainWindow *ui_; + Scene *scene_; + PacmanGame *game_; + int rotationAngle_ = 90; + float itemScaleFactor_ = 0.1; +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index b232854..3f00680 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -1,22 +1,215 @@ - - - MainWindow - - - - 0 - 0 - 800 - 600 - - - - MainWindow - - - - - - - - + + + MainWindow + + + + 0 + 0 + 979 + 600 + + + + MainWindow + + + + + + 549 + 0 + 290 + 541 + + + + + + + Начать игру! + + + + + + + GroupBox + + + + + 20 + 40 + 201 + 31 + + + + Новая стена + + + + + + 20 + 80 + 201 + 31 + + + + Пакман + + + + + + 20 + 120 + 201 + 31 + + + + Монетка + + + + + + + + Взаимодействие с выделенными объектами + + + + + 10 + 110 + 231 + 121 + + + + Размер фигуры + + + + + 0 + 20 + 231 + 51 + + + + + + + + + + + + + + + - + + + + + + + + + 40 + 80 + 161 + 31 + + + + Сбросить + + + + + + + 10 + 30 + 221 + 25 + + + + Повернуть + + + + + + 9 + 60 + 221 + 25 + + + + Удалить + + + + + + + + + + 0 + 0 + 541 + 541 + + + + + + + 840 + 80 + 131 + 91 + + + + + + + 890 + 60 + 55 + 16 + + + + Очки: + + + + + + + 0 + 0 + 979 + 25 + + + + + + + + diff --git a/pacmangame.cpp b/pacmangame.cpp new file mode 100644 index 0000000..393b2a5 --- /dev/null +++ b/pacmangame.cpp @@ -0,0 +1,131 @@ +#include "pacmangame.h" + +#include +#include + +#include +#include + +PacmanGame::PacmanGame(Scene &scene, QWidget *parent) : QWidget(parent), scene_(scene) { + collisionColor_ = Qt::red; + originColor_ = + ItemsFactory::GetColorByItemType(ItemsFactory::ItemsType::kPlayer); +} + +void PacmanGame::startNewGame() { + player_ = scene_.getPlayer(); + + if (!player_) { + QMessageBox::critical(this, "Error", "Поместите Пакмана на игровое поле!"); + return; + } + + MainWindow *mainWindow = qobject_cast(parent()); + + const QList &coins = scene_.getCoins(); + + if (coins.isEmpty()) { + if (mainWindow) { + QMessageBox::critical(this, "Error", "Необходимо разместить монеты на игровом поле!"); + mainWindow->SetWidgetMoveEnabled(true); + } + return; + } + + if (gameTimer_.isActive()) { + QMessageBox::critical(this, "Error", "Игра стартовала!"); + return; + } + + if (mainWindow) { + mainWindow->SetWidgetMoveEnabled(false); + } + + score_ = 0; + updateScore(score_); + gameTimer_.start(intervalTime_, this); + player_ = scene_.getPlayer(); +} + +void PacmanGame::stopGame() { + if (!gameTimer_.isActive()) { + QMessageBox::critical(this, "Error", "Игра стартовала!"); + return; + } + gameTimer_.stop(); +} + +void PacmanGame::updateScore(int score) { + score_ += score; + emit initScoreChanged(score_); +} + +void PacmanGame::initGameEnd() { + scene_.setFiguresDragAndDropOption(true); + + MainWindow *mainWindow = qobject_cast(parent()); + if (mainWindow) { + mainWindow->SetWidgetMoveEnabled(true); + } + + score_ = 0; + + QMessageBox::information(this, "Победа!", "Победа!"); +} + +void PacmanGame::timerEvent(QTimerEvent *event) { + if (event->timerId() == gameTimer_.timerId() && player_) { + int newX = player_->pos().x() + player_->getCoordinateX() * player_->getStep(); + int newY = player_->pos().y() + player_->getCoordinateY() * player_->getStep(); + + if (isOutOfBounds(newX, newY) || hasCollision()) { + player_->invertCoordinates(); + } + player_->movePlayer(); + } else { + QWidget::timerEvent(event); + } +} + +bool PacmanGame::isOutOfBounds(int newX, int newY) { + const QRectF sceneRect = scene_.sceneRect(); + QRectF playerRect(newX, newY, player_->sceneBoundingRect().width(), player_->sceneBoundingRect().height()); + + if (newX < sceneRect.left() || + newX + playerRect.width() > sceneRect.right() || newY < sceneRect.top() || + newY + playerRect.height() > sceneRect.bottom()) { + return true; + } + + return false; +} + +bool PacmanGame::hasCollision() { + const QList &walls = scene_.getWalls(); + const QList &coins = scene_.getCoins(); + + for (PacmanItem *wall : walls) { + if (player_->collidesWithItem(wall)) { + return true; + } + } + + for (PacmanItem *coin : coins) { + if (player_->collidesWithItem(coin)) { + deleteCoin(coin); + } + } + return false; +} + +void PacmanGame::deleteCoin(PacmanItem *coin) { + if (scene_.deleteCoin(coin)) { + int score = 1; + updateScore(score); + + if (scene_.getCoins().isEmpty()) { + initGameEnd(); + stopGame(); + } + } +} diff --git a/pacmangame.h b/pacmangame.h new file mode 100644 index 0000000..c1d7336 --- /dev/null +++ b/pacmangame.h @@ -0,0 +1,47 @@ +#ifndef PACMANGAME_H +#define PACMANGAME_H + +#include +#include +#include + +#include "itemsfactory.h" +#include "scene.h" + +class PacmanGame : public QWidget { + Q_OBJECT + + public: + PacmanGame(Scene& scene, QWidget* parent = nullptr); + + public slots: + void startNewGame(); + void stopGame(); + + signals: + void initScoreChanged(uint score); + + protected: + void timerEvent(QTimerEvent* event); + + private: + void initGameEnd(); + void updateScore(int addScore); + void deleteCoin(PacmanItem* coin); + bool hasCollision(); + bool isOutOfBounds(int newX, int newY); + + + private: + uint score_; + + Scene& scene_; + PacmanItem* player_; + + QColor collisionColor_; + QColor originColor_; + const uint intervalTime_ = 10; + QBasicTimer gameTimer_; +}; + +#endif // PACMANGAME_H diff --git a/pacmanitem.cpp b/pacmanitem.cpp new file mode 100644 index 0000000..ee7c543 --- /dev/null +++ b/pacmanitem.cpp @@ -0,0 +1,78 @@ +#include "pacmanitem.h" + +#include + +PacmanItem::PacmanItem(QPainterPath geometry, QPen pen, QBrush brush) + : QAbstractGraphicsShapeItem(), + geometry_(geometry), + selectedPenFront_(pen), + selectedPenBack_(pen) { + + setPen(pen); + setBrush(brush); + + coordinateX_ = 1; + coordinateY_ = 0; + step_ = 1; + + selectedPenBack_ = QPen(kSelectedPenColor_, pen.width(), Qt::SolidLine); + selectedPenFront_ = QPen(pen.color(), pen.width(), kSelectedPenStyle_); + + setFlags(ItemIsMovable | ItemIsSelectable); +} + +QPainterPath PacmanItem::shape() const { return geometry_; } + +QRectF PacmanItem::boundingRect() const { + int divider = 2; + qreal penThickness = pen().widthF(); + + return geometry_.boundingRect().adjusted(-penThickness / divider, + -penThickness / divider, + penThickness / divider, + penThickness / divider); +} + +void PacmanItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) { + Q_UNUSED(option); + Q_UNUSED(widget); + + painter->setRenderHint(QPainter::Antialiasing); + + painter->setPen(pen()); + painter->setBrush(brush()); + painter->drawPath(geometry_); + + if (isSelected()) { + painter->setPen(selectedPenBack_); + DrawBorder(painter); + + painter->setPen(selectedPenFront_); + DrawBorder(painter); + } +} + +uint PacmanItem::getCoordinateX() { return coordinateX_; } + +uint PacmanItem::getCoordinateY() { return coordinateY_; } + +uint PacmanItem::getStep() { return step_; } + +void PacmanItem::setCoordinates(uint x, uint y) { + coordinateX_ = x; + coordinateY_ = y; +} + +void PacmanItem::movePlayer() { + int newCoordinateX = pos().x() + coordinateX_ * step_; + int newCoordinateY = pos().y() + coordinateY_ * step_; + setPos(newCoordinateX, newCoordinateY); +} + +void PacmanItem::invertCoordinates() { + coordinateX_ = -coordinateX_; + coordinateY_ = -coordinateY_; +} + +void PacmanItem::DrawBorder(QPainter *painter) { painter->drawPath(geometry_); } diff --git a/pacmanitem.h b/pacmanitem.h new file mode 100644 index 0000000..dbb066f --- /dev/null +++ b/pacmanitem.h @@ -0,0 +1,42 @@ +#ifndef PACMANITEM_H +#define PACMANITEM_H + +#include +#include +#include + +class PacmanItem : public QAbstractGraphicsShapeItem { + public: + explicit PacmanItem(QPainterPath geometry, QPen pen, QBrush brush); + + public: + QPainterPath shape() const; + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget); + + void setCoordinates(uint x, uint y); + void movePlayer(); + + uint getCoordinateX(); + uint getCoordinateY(); + uint getStep(); + void invertCoordinates(); + + private: + void DrawBorder(QPainter *painter); + + private: + const QColor kSelectedPenColor_ = Qt::white; + const Qt::PenStyle kSelectedPenStyle_ = Qt::DashLine; + + QPainterPath geometry_; + QPen selectedPenFront_; + QPen selectedPenBack_; + + uint coordinateX_; + uint coordinateY_; + uint step_; +}; + +#endif // PACMANITEM_H diff --git a/scene.cpp b/scene.cpp new file mode 100644 index 0000000..f5ba6da --- /dev/null +++ b/scene.cpp @@ -0,0 +1,123 @@ +#include "scene.h" + +#include + +#include +#include +#include +#include +#include + +#include "qmimedata.h" +#include "dragitem.h" + +Scene::Scene(QObject *parent) : QGraphicsScene{parent} { player_ = nullptr; } + +void Scene::keyPressEvent(QKeyEvent *event) { + int deltaTimerInterval = 50; + int positionFirst = 2; + int positionSecond = 0; + + if (event->key() == Qt::Key_Delete) { + RemoveSelectedItems(); + + } else if (event->key() == Qt::Key_Down) { + player_->setCoordinates(0, 1); + + } else if (event->key() == Qt::Key_Up) { + player_->setCoordinates(0, -1); + + } else if (event->key() == Qt::Key_Left) { + player_->setCoordinates(-1, 0); + + } else if (event->key() == Qt::Key_Right) { + player_->setCoordinates(1, 0); + } +} + +const QList &Scene::getWalls() const { return walls_; } +const QList &Scene::getCoins() const { return coins_; } +PacmanItem *Scene::getPlayer() const { return player_; } + +bool Scene::deleteCoin(PacmanItem *coin) { + if (!coins_.contains(coin)) { + return false; + } + removeItem(coin); + coins_.removeOne(coin); + delete coin; + return true; +} + +void Scene::RotateSelectedItems() { + int angle = 90; + + for (auto &item : selectedItems()) { + item->setRotation(item->rotation() + angle); + } +} + +void Scene::ScaleSelectedItems(qreal scaleFactor) { + for (auto &item : selectedItems()) { + item->setScale(item->scale() + scaleFactor); + } +} + +void Scene::RemoveSelectedItems() { + for (auto &item : selectedItems()) { + removeItem(item); + } +} + +void Scene::ResetSelectedItemsScale() { + int defaultScale = 1; + + for (auto &item : selectedItems()) { + item->setScale(defaultScale); + } +} + +void Scene::dragEnterEvent(QGraphicsSceneDragDropEvent *event) { + if (event->mimeData()->hasFormat(DragItem::kMimeFormat)) { + event->acceptProposedAction(); + } else { + event->ignore(); + } +} + +void Scene::dragMoveEvent(QGraphicsSceneDragDropEvent *event) { + if (event->mimeData()->hasFormat(DragItem::kMimeFormat)) { + event->accept(); + } +} + +void Scene::dropEvent(QGraphicsSceneDragDropEvent *event) { + if (event->mimeData()->hasFormat(DragItem::kMimeFormat)) { + /* + *Если формат mime нам подходит, то считываем необходимые данные (тип + *фигуры), которые Вносили в DragItem + */ + QByteArray itemData = event->mimeData()->data(DragItem::kMimeFormat); + QDataStream dataStream(&itemData, QIODevice::ReadOnly); + + ItemsFactory::ItemsType itemType; + // Записываем значение переданного типа из потока. + dataStream >> itemType; + + // Создаем объект согласно типу и размещаем его относительно положения мыши + // в момент отпускания левой кнопки мыши + PacmanItem *item = ItemsFactory::Create(itemType); + item->setPos(event->scenePos()); + + // Добавляем объект на сцену + addItem(item); + + event->acceptProposedAction(); + } else { + event->ignore(); + } +} + + + + diff --git a/scene.h b/scene.h new file mode 100644 index 0000000..4c45c9e --- /dev/null +++ b/scene.h @@ -0,0 +1,38 @@ +#ifndef SCENE_H +#define SCENE_H + +#include +#include +#include "pacmanitem.h" + +class Scene : public QGraphicsScene { + // Q_OBJECT + public: + explicit Scene(QObject *parent = nullptr); + const QList &getWalls() const; + const QList &getCoins() const; + PacmanItem *getPlayer() const; + + void setFiguresDragAndDropOption(bool enable); + bool deleteCoin(PacmanItem *coin); + + public slots: + void RotateSelectedItems(); + void ScaleSelectedItems(qreal scaleFactor); + void RemoveSelectedItems(); + void ResetSelectedItemsScale(); + + protected: + void keyPressEvent(QKeyEvent *event); + void dragEnterEvent(QGraphicsSceneDragDropEvent *event); + void dragMoveEvent(QGraphicsSceneDragDropEvent *event); + void dropEvent(QGraphicsSceneDragDropEvent *event); + + private: + QList walls_; + QList coins_; + PacmanItem *player_; + // QTimer* pacmanMoveTimer_; +}; + +#endif // SCENE_H From 115ecfb10b8f02c3069b38bbfe4fd144f343afa9 Mon Sep 17 00:00:00 2001 From: Alina <99394498+alepopas@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:22:00 +0300 Subject: [PATCH 2/2] Add files via upload --- dragitem.cpp | 5 +---- mainwindow.cpp | 56 +++++++++++++++++++++++++++++-------------------- mainwindow.h | 3 ++- mainwindow.ui | 4 ++-- pacmangame.cpp | 6 +----- pacmanitem.cpp | 3 +-- scene.cpp | 57 +++++++++++++++++++++++++++++--------------------- scene.h | 5 +++-- 8 files changed, 76 insertions(+), 63 deletions(-) diff --git a/dragitem.cpp b/dragitem.cpp index 0b30887..749aaed 100644 --- a/dragitem.cpp +++ b/dragitem.cpp @@ -11,13 +11,10 @@ DragItem::DragItem(ItemsFactory::ItemsType type, QWidget *parent) QRect geometry = graphicsItem_->boundingRect().toRect(); setGeometry(geometry); - // Задаем размер элемента списка (QListWidgetItem) - // Делаем его чуть больше в размере, чтобы элементы списка не слипались - int additionalHeight = 2; + int additionalHeight = 20; setSizeHint(QSize(geometry.width(), geometry.height() + additionalHeight)); } -// Отрисовываем нашу фигуру в списке void DragItem::paintEvent(QPaintEvent *event) { Q_UNUSED(event) diff --git a/mainwindow.cpp b/mainwindow.cpp index e2c9c3a..98b754b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -3,8 +3,11 @@ #include #include +#include +#include #include #include +#include #include "ui_mainwindow.h" @@ -16,7 +19,7 @@ MainWindow::MainWindow(QWidget *parent) SetConnections(); ui_->graphicsView->setFocusPolicy(Qt::StrongFocus); - ui_->listWidget->setFocusPolicy(Qt::NoFocus); + //ui_->listWidget->setFocusPolicy(Qt::NoFocus); } MainWindow::~MainWindow() { delete ui_; } @@ -38,16 +41,30 @@ void MainWindow::AddNewItem() { QGraphicsItem::ItemIsSelectable); } -void MainWindow::AddNewOwnItem(ItemsFactory::ItemsType itemType) { +//void MainWindow::AddItemToList(ItemsFactory::ItemsType itemType) { +// DragItem *item = new DragItem(itemType); +// ui_->listWidget->addItem(item); +// ui_->listWidget->setItemWidget(item, item); +//} +void MainWindow::AddNewCoinItem() { + scene_->createEvent(ItemsFactory::ItemsType::kCoin); + qDebug() << scene_->getWalls() << scene_->getCoins() << scene_->getPlayer(); + // scene_->addItem(ItemsFactory::Create(ItemsFactory::ItemsType::kCoin)); } -void MainWindow::AddNewCoinItem() { - scene_->addItem(ItemsFactory::Create(ItemsFactory::ItemsType::kCoin)); +void MainWindow::AddNewWallItem() { + scene_->createEvent(ItemsFactory::ItemsType::kWall); + // scene_->addItem(ItemsFactory::Create(ItemsFactory::ItemsType::kWall)); +} + +void MainWindow::AddNewPlayerItem() { + scene_->createEvent(ItemsFactory::ItemsType::kPlayer); + // scene_->addItem(ItemsFactory::Create(ItemsFactory::ItemsType::kPlayer)); } void MainWindow::UpdateScoreLSD(uint score) { - ui_->Score->display(score); + //ui_->scoreLCD->display(score); } void MainWindow::keyPressEvent(QKeyEvent *event) { @@ -56,20 +73,14 @@ void MainWindow::keyPressEvent(QKeyEvent *event) { // Если есть выделенные объекты на сцене, берем первый из них if (!selectedItems.isEmpty()) { auto item = selectedItems[0]; - - // Выводим все столкновения объекта item на сцене с помощью метода сцены - qDebug() << "Метод сцены: " << scene_->collidingItems(item) - << Qt::endl - // Для выбранного объекта выводим все столкновения c помощью - // метода объекта - << "Метод объекта: " << item->collidingItems(); - - // Проверяем сталкивается ли объект item с item2 - // item->collidesWithItem(item2) } } } +void MainWindow::SetWidgetMoveEnabled(bool enabled) { + +} + void MainWindow::SetConnections() { connect(ui_->startButton, &QPushButton::clicked, game_, @@ -78,10 +89,10 @@ void MainWindow::SetConnections() { [=]() { scene_->setFiguresDragAndDropOption(false); }); connect(ui_->newItemButon, &QPushButton::clicked, this, - &MainWindow::AddNewItem); + &MainWindow::AddNewWallItem); - connect(ui_->newOwnItemButton, &QPushButton::clicked, this, - &MainWindow::AddNewOwnItem); + connect(ui_->newPlayerButton, &QPushButton::clicked, this, + &MainWindow::AddNewPlayerItem); connect(ui_->newCoinItemButton, &QPushButton::clicked, this, &MainWindow::AddNewCoinItem); @@ -118,14 +129,13 @@ void MainWindow::SetUi() { QRectF viewRect = ui_->graphicsView->geometry(); scene_->setSceneRect(viewRect); - AddNewOwnItem(ItemsFactory::ItemsType::kWall); - AddNewOwnItem(ItemsFactory::ItemsType::kPlayer); - AddNewOwnItem(ItemsFactory::ItemsType::kCoin); +// AddNewOwnItem(ItemsFactory::ItemsType::kWall); +// AddNewOwnItem(ItemsFactory::ItemsType::kPlayer); +// AddNewOwnItem(ItemsFactory::ItemsType::kCoin); } void MainWindow::addItemToList(ItemsFactory::ItemsType itemType) { DragItem *item = new DragItem(itemType); - ui_->listWidget->addItem(item); - ui_->listWidget->setItemWidget(item, item); + } diff --git a/mainwindow.h b/mainwindow.h index 6b2d0aa..42d4a06 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -24,8 +24,9 @@ class MainWindow : public QMainWindow { private slots: void UpdateScoreLSD(uint newScore); void AddNewItem(); - void AddNewOwnItem(); + void AddNewPlayerItem(); void AddNewCoinItem(); + void AddNewWallItem(); protected: void keyPressEvent(QKeyEvent *event); diff --git a/mainwindow.ui b/mainwindow.ui index 3f00680..990b568 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -49,7 +49,7 @@ Новая стена - + 20 @@ -174,7 +174,7 @@ - + 840 diff --git a/pacmangame.cpp b/pacmangame.cpp index 393b2a5..9643d6f 100644 --- a/pacmangame.cpp +++ b/pacmangame.cpp @@ -27,7 +27,7 @@ void PacmanGame::startNewGame() { if (coins.isEmpty()) { if (mainWindow) { QMessageBox::critical(this, "Error", "Необходимо разместить монеты на игровом поле!"); - mainWindow->SetWidgetMoveEnabled(true); + //mainWindow->SetWidgetMoveEnabled(true); } return; } @@ -37,10 +37,6 @@ void PacmanGame::startNewGame() { return; } - if (mainWindow) { - mainWindow->SetWidgetMoveEnabled(false); - } - score_ = 0; updateScore(score_); gameTimer_.start(intervalTime_, this); diff --git a/pacmanitem.cpp b/pacmanitem.cpp index ee7c543..12fd735 100644 --- a/pacmanitem.cpp +++ b/pacmanitem.cpp @@ -11,7 +11,7 @@ PacmanItem::PacmanItem(QPainterPath geometry, QPen pen, QBrush brush) setPen(pen); setBrush(brush); - coordinateX_ = 1; + coordinateX_ = 7; coordinateY_ = 0; step_ = 1; @@ -43,7 +43,6 @@ void PacmanItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option painter->setPen(pen()); painter->setBrush(brush()); painter->drawPath(geometry_); - if (isSelected()) { painter->setPen(selectedPenBack_); DrawBorder(painter); diff --git a/scene.cpp b/scene.cpp index f5ba6da..32413ce 100644 --- a/scene.cpp +++ b/scene.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "qmimedata.h" #include "dragitem.h" @@ -85,37 +86,45 @@ void Scene::dragEnterEvent(QGraphicsSceneDragDropEvent *event) { } } +void Scene::setFiguresDragAndDropOption(bool enable) { + for (auto &item : items()) { + qDebug() << item; + item->setFlag(QGraphicsItem::ItemIsMovable, enable); + item->setFlag(QGraphicsItem::ItemIsSelectable, enable); + item->setFlag(QGraphicsItem::ItemIsFocusable, enable); + } +} + void Scene::dragMoveEvent(QGraphicsSceneDragDropEvent *event) { if (event->mimeData()->hasFormat(DragItem::kMimeFormat)) { event->accept(); } } -void Scene::dropEvent(QGraphicsSceneDragDropEvent *event) { - if (event->mimeData()->hasFormat(DragItem::kMimeFormat)) { - /* - *Если формат mime нам подходит, то считываем необходимые данные (тип - *фигуры), которые Вносили в DragItem - */ - QByteArray itemData = event->mimeData()->data(DragItem::kMimeFormat); - QDataStream dataStream(&itemData, QIODevice::ReadOnly); - - ItemsFactory::ItemsType itemType; - // Записываем значение переданного типа из потока. - dataStream >> itemType; - - // Создаем объект согласно типу и размещаем его относительно положения мыши - // в момент отпускания левой кнопки мыши - PacmanItem *item = ItemsFactory::Create(itemType); - item->setPos(event->scenePos()); - - // Добавляем объект на сцену - addItem(item); - - event->acceptProposedAction(); - } else { - event->ignore(); +void Scene::createEvent(ItemsFactory::ItemsType itemType) { + PacmanItem *item = ItemsFactory::Create(itemType); + + switch (itemType) { + case ItemsFactory::ItemsType::kWall: + walls_.append(item); + break; + case ItemsFactory::ItemsType::kCoin: + coins_.append(item); + break; + case ItemsFactory::ItemsType::kPlayer: + if (player_ == nullptr) { + player_ = item; + } else { + QMessageBox::information(nullptr, "Ошибка", "На сцене может быть только один игрок."); + return; + } + break; + + default: + break; } + + addItem(item); } diff --git a/scene.h b/scene.h index 4c45c9e..c048e9a 100644 --- a/scene.h +++ b/scene.h @@ -4,6 +4,7 @@ #include #include #include "pacmanitem.h" +#include "itemsfactory.h" class Scene : public QGraphicsScene { // Q_OBJECT @@ -12,7 +13,7 @@ class Scene : public QGraphicsScene { const QList &getWalls() const; const QList &getCoins() const; PacmanItem *getPlayer() const; - + void createEvent(ItemsFactory::ItemsType itemType); void setFiguresDragAndDropOption(bool enable); bool deleteCoin(PacmanItem *coin); @@ -22,11 +23,11 @@ class Scene : public QGraphicsScene { void RemoveSelectedItems(); void ResetSelectedItemsScale(); + protected: void keyPressEvent(QKeyEvent *event); void dragEnterEvent(QGraphicsSceneDragDropEvent *event); void dragMoveEvent(QGraphicsSceneDragDropEvent *event); - void dropEvent(QGraphicsSceneDragDropEvent *event); private: QList walls_;