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..749aaed --- /dev/null +++ b/dragitem.cpp @@ -0,0 +1,31 @@ +#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); + + int additionalHeight = 20; + 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..98b754b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,15 +1,141 @@ -#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 +#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::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::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_->scoreLCD->display(score); +} + +void MainWindow::keyPressEvent(QKeyEvent *event) { + if (event->key() == Qt::Key_Space) { + auto selectedItems = scene_->selectedItems(); + // Если есть выделенные объекты на сцене, берем первый из них + if (!selectedItems.isEmpty()) { + auto item = selectedItems[0]; + } + } +} + +void MainWindow::SetWidgetMoveEnabled(bool enabled) { + +} + +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::AddNewWallItem); + + connect(ui_->newPlayerButton, &QPushButton::clicked, this, + &MainWindow::AddNewPlayerItem); + + 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); + +} + diff --git a/mainwindow.h b/mainwindow.h index dbe42ab..42d4a06 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -1,21 +1,46 @@ -#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 AddNewPlayerItem(); + void AddNewCoinItem(); + void AddNewWallItem(); + + 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..990b568 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..9643d6f --- /dev/null +++ b/pacmangame.cpp @@ -0,0 +1,127 @@ +#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; + } + + 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..12fd735 --- /dev/null +++ b/pacmanitem.cpp @@ -0,0 +1,77 @@ +#include "pacmanitem.h" + +#include + +PacmanItem::PacmanItem(QPainterPath geometry, QPen pen, QBrush brush) + : QAbstractGraphicsShapeItem(), + geometry_(geometry), + selectedPenFront_(pen), + selectedPenBack_(pen) { + + setPen(pen); + setBrush(brush); + + coordinateX_ = 7; + 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..32413ce --- /dev/null +++ b/scene.cpp @@ -0,0 +1,132 @@ +#include "scene.h" + +#include + +#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::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::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 new file mode 100644 index 0000000..c048e9a --- /dev/null +++ b/scene.h @@ -0,0 +1,39 @@ +#ifndef SCENE_H +#define SCENE_H + +#include +#include +#include "pacmanitem.h" +#include "itemsfactory.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 createEvent(ItemsFactory::ItemsType itemType); + 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); + + private: + QList walls_; + QList coins_; + PacmanItem *player_; + // QTimer* pacmanMoveTimer_; +}; + +#endif // SCENE_H