diff --git a/TetrisTask.pro b/TetrisTask.pro index f4de819..041fcb4 100644 --- a/TetrisTask.pro +++ b/TetrisTask.pro @@ -9,16 +9,15 @@ CONFIG += c++17 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ - gamefield.cpp \ main.cpp \ - tetris.cpp + mainwindow.cpp \ + tetrisgrid.cpp HEADERS += \ - gamefield.h \ - tetris.h + mainwindow.h \ + tetrisgrid.h -FORMS += \ - tetris.ui +FORMS += # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin diff --git a/gamefield.cpp b/gamefield.cpp deleted file mode 100644 index 6a5ce13..0000000 --- a/gamefield.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "gamefield.h" - -#include - -GameField::GameField(QWidget *parent) : QWidget{parent} { - connect(this, &GameField::InitialisationStarted, this, &GameField::SetCells, - Qt::QueuedConnection); - - emit InitialisationStarted(); -} - -uint GameField::GetRowsNumber() const { return rowsNumber_; } - -void GameField::SetRowsNumber(uint rowsNumber) { - if (rowsNumber_ == rowsNumber) return; - - rowsNumber_ = rowsNumber; - emit RowsNumberChanged(); -} - -uint GameField::GetColumnsNumber() const { return columnsNumber_; } - -void GameField::SetColumnNumber(uint columnsCount) { - if (columnsNumber_ == columnsCount) return; - - columnsNumber_ = columnsCount; - emit ColumnsNumberChanged(); -} - -void GameField::SetCells() { - qDebug() << rowsNumber_ << " " << columnsNumber_ << "|"; -} diff --git a/gamefield.h b/gamefield.h deleted file mode 100644 index e36baa8..0000000 --- a/gamefield.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef GAMEFIELD_H -#define GAMEFIELD_H - -#include - -class GameField : public QWidget { - Q_OBJECT - public: - explicit GameField(QWidget *parent = nullptr); - - Q_PROPERTY(uint rowsNumber READ GetRowsNumber WRITE SetRowsNumber NOTIFY - RowsNumberChanged FINAL) - Q_PROPERTY(uint columnsNumber READ GetColumnsNumber WRITE SetColumnNumber - NOTIFY ColumnsNumberChanged FINAL) - - signals: - void RowsNumberChanged(); - void ColumnsNumberChanged(); - void InitialisationStarted(); - - private slots: - void SetCells(); - - public: - uint GetRowsNumber() const; - void SetRowsNumber(uint newRowsNumber); - - uint GetColumnsNumber() const; - void SetColumnNumber(uint newColumnsCount); - - private: - uint rowsNumber_ = 0; - uint columnsNumber_ = 0; -}; - -#endif // GAMEFIELD_H diff --git a/main.cpp b/main.cpp index f1c8bd9..2e33976 100644 --- a/main.cpp +++ b/main.cpp @@ -1,10 +1,11 @@ #include - -#include "tetris.h" +#include +#include "mainwindow.h" int main(int argc, char *argv[]) { - QApplication a(argc, argv); - Tetris w; - w.show(); - return a.exec(); + QApplication a(argc, argv); + MainWindow w; + w.resize(400, 500); + w.show(); + return a.exec(); } diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..2208300 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,112 @@ +#include "mainwindow.h" +#include +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), score_(0) { + isGameOverHandled_ = false; + tetrisGrid_ = new TetrisGrid(this); + startButton_ =new QPushButton("Начать новую игру", this); + exitButton_ = new QPushButton("Выход", this); + scoreLabel_ = new QLabel("Счетчик очков: 0", this); + helpButton_ = new QPushButton("Справка", this); + + qApp->setStyle(QStyleFactory::create("Fusion")); + QGraphicsDropShadowEffect *shadowEffect = new QGraphicsDropShadowEffect(this); + shadowEffect->setBlurRadius(15); + shadowEffect->setColor(QColor(0, 0, 0, 150)); + shadowEffect->setOffset(0, 5); + + QString buttonStyle = "QPushButton {" + " background-color: #3498db;" + " border-style: outset;" + " border-width: 2px;" + " border-radius: 10px;" + " border-color: #2980b9;" + " color: #ecf0f1;" + " padding: 5px;" + "}" + "QPushButton:hover {" + " background-color: #2980b9;" + " color: #bdc3c7;" + "}"; + startButton_->setStyleSheet(buttonStyle); + startButton_->setGraphicsEffect(shadowEffect); + helpButton_->setStyleSheet(buttonStyle); + helpButton_->setGraphicsEffect(shadowEffect); + exitButton_->setStyleSheet(buttonStyle); + exitButton_->setGraphicsEffect(shadowEffect); + + connect(helpButton_, &QPushButton::clicked, this, &MainWindow::showHelp); + connect(startButton_, &QPushButton::clicked, this, &MainWindow::restartGame); + connect(exitButton_, &QPushButton::clicked, this, &MainWindow::exitGame); + connect(tetrisGrid_, SIGNAL(scoreChanged(int)), this, SLOT(updateScore(int))); + connect(tetrisGrid_, &TetrisGrid::gameOver, [=]() { + if (!isGameOverHandled_) { + isGameOverHandled_ = true; + exitGame(); + } + }); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(tetrisGrid_, Qt::AlignHCenter); + layout->addWidget(scoreLabel_); + layout->addWidget(startButton_); + layout->addWidget(helpButton_); + layout->addWidget(exitButton_); + + QWidget *centralWidget = new QWidget(this); + centralWidget->setLayout(layout); + setCentralWidget(centralWidget); +} + +MainWindow::~MainWindow() {} + +void MainWindow::showHelp() { + QString helpText = "Правила игры:\n\n" + "1. Сдвигайте фигуры влево/вправо, используя стрелки клавиатуры.\n" + "2. Поворачивайте фигуры при помощи клавиши 'Вверх'.\n" + "3. Ускоряйте падение фигур с помощью кнопки 'Вниз'.\n" + "4. Заполняйте горизонтальные линии, чтобы они исчезали и приносили очки.\n" + "\nУправление:\n" + "- Сдвиг влево: Стрелка влево\n" + "- Сдвиг вправо: Стрелка вправо\n" + "- Поворот: Стрелка вверх\n" + "- Ускорение падения: Стрелка вниз'\n"; + QMessageBox::information(this, "Справка", helpText); +} + +void MainWindow::restartGame() { + tetrisGrid_->startGame(); + setupGame(); +} + +void MainWindow::startGame() { + setFocus(); + score_ = 0; + updateScore(score_); +} + +void MainWindow::exitGame() { + isGameOverHandled_ = true; + QMessageBox messageBox; + messageBox.setWindowTitle("Игра окончена"); + messageBox.setText("Игра завершена. Набрано очков: " + QString::number(score_)); + messageBox.setStandardButtons(QMessageBox::Ok); + messageBox.exec(); + close(); +} + +void MainWindow::updateScore(int newScore) { + score_ = newScore; + scoreLabel_->setText("Счетчик очков: " + QString::number(score_)); +} + +void MainWindow::keyPressEvent(QKeyEvent *event) { + tetrisGrid_->handleKeyPressEvent(event); +} + +void MainWindow::setupGame() {} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..d600a8b --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include "tetrisgrid.h" + +class MainWindow : public QMainWindow { + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + void setupGame(); + int score_; + bool isGameOverHandled_ = false; + TetrisGrid *tetrisGrid_; + QPushButton *startButton_; + QPushButton *exitButton_; + QPushButton *helpButton_; + QLabel *scoreLabel_; + +private slots: + void startGame(); + void exitGame(); + void updateScore(int newScore); + void restartGame(); + void showHelp(); + +protected: + void keyPressEvent(QKeyEvent *event); +}; + +#endif // MAINWINDOW_H diff --git a/tetris.ui b/tetris.ui deleted file mode 100644 index ae63379..0000000 --- a/tetris.ui +++ /dev/null @@ -1,46 +0,0 @@ - - - Tetris - - - - 0 - 0 - 800 - 600 - - - - Tetris - - - - 20 - - - 10 - - - - - - 0 - 0 - 800 - 21 - - - - - - - - GameField - QWidget -
gamefield.h
- 1 -
-
- - -
diff --git a/tetrisgrid.cpp b/tetrisgrid.cpp new file mode 100644 index 0000000..1b22ee1 --- /dev/null +++ b/tetrisgrid.cpp @@ -0,0 +1,301 @@ +#include "tetrisgrid.h" +#include +#include +#include +#include + +TetrisGrid::TetrisGrid(QWidget *parent) + : QWidget(parent), mRows_(24), mColumns_(10), timer_(new QTimer(this)), score_(0) { + mGrid_.resize(mRows_, QVector(mColumns_, 0)); + setFocusPolicy(Qt::StrongFocus); + connect(timer_, &QTimer::timeout, this, &TetrisGrid::dropShape); + timer_->setInterval(speed_); + startGame(); +} + +void TetrisGrid::addCurrentShapeToGridNew() { + for (int i = 0; i < currentShape_.size(); ++i) { + for (int j = 0; j < currentShape_[i].size(); ++j) { + if (currentShape_[i][j] == 1) { + int gridRow = shapeRow_ + i; + int gridColumn = shapeColumn_ + j; + if (gridRow >= 0 && gridRow < mRows_ && gridColumn >= 0 && gridColumn < mColumns_) { + if (gridRow <= 4) { + emit gameOver(); + } + else + mGrid_[gridRow][gridColumn] = 1; + } + } + } + } +} + +void TetrisGrid::setupGame() { + mGrid_.clear(); + mGrid_.resize(mRows_, QVector(mColumns_, 0)); + score_ = 0; + updateScore(score_); + generateNewShape(); +} + +void TetrisGrid::speedFigure(){ + timer_->setInterval(100); + timer_->start(); +} + +int TetrisGrid::rows() const { + return mRows_; +} + +int TetrisGrid::columns() const { + return mColumns_; +} + +void TetrisGrid::setRows(int rows) { + if (mRows_ != rows) { + mRows_ = rows; + mGrid_.resize(mRows_); + for (auto &row : mGrid_) { + row.resize(mColumns_); + } + emit rowsChanged(mRows_); + update(); + } +} + +void TetrisGrid::setColumns(int columns) { + if (mColumns_ != columns) { + mColumns_ = columns; + for (auto &row_ : mGrid_) { + row_.resize(mColumns_); + } + emit columnsChanged(mColumns_); + update(); + } +} + +void TetrisGrid::startGame() { + setupGame(); + timer_->start(); +} + +void TetrisGrid::moveShapeLeft() { + if (shapeColumn_ > 0) { + --shapeColumn_; + if (!isValidMove(currentShape_, shapeRow_, shapeColumn_)) { + ++shapeColumn_; + } + update(); + } +} + +void TetrisGrid::moveShapeRight() { + if (shapeColumn_ + currentShape_[0].size() < mColumns_) { + ++shapeColumn_; + if (!isValidMove(currentShape_, shapeRow_, shapeColumn_)) { + --shapeColumn_; + } + update(); + } +} + +bool TetrisGrid::isValidMove(const QVector> &shape, int rows, int columns) { + for (int i = 0; i < shape.size(); ++i) { + for (int j = 0; j < shape[i].size(); ++j) { + if (shape[i][j] == 1) { + int gridRow = rows + i; + int gridColumn = columns + j; + if (gridRow < 0 || gridRow >= mRows_ || gridColumn < 0 || gridColumn >= mColumns_) { + return false; + } + if (mGrid_[gridRow][gridColumn] == 1) { + return false; + } + } + } + } + return true; +} + +void TetrisGrid::rotateShape() +{ + QVector> rotatedShape; + QVector currentRow; + for (int j = currentShape_[0].size() - 1; j >= 0; --j) { + currentRow.clear(); + for (int i = 0; i < currentShape_.size(); ++i) { + currentRow.append(currentShape_[i][j]); + } + rotatedShape.append(currentRow); + } + if (isValidMove(rotatedShape, shapeRow_, shapeColumn_)) { + currentShape_ = rotatedShape; + update(); + } +} + +void TetrisGrid::dropShape() { + if (shapeRow_ + currentShape_.size() <= mRows_) { + ++shapeRow_; + if (isCollision()) { + --shapeRow_; + addCurrentShapeToGridNew(); + generateNewShape(); + checkLines(); + } + update(); + } +} + +void TetrisGrid::updateScore(int newScore) { + score_ = newScore; + emit scoreChanged(score_); +} + +void TetrisGrid::paintEvent(QPaintEvent *event) { + Q_UNUSED(event); + QPainter painter(this); + int cellSize = qMin(width() / mColumns_, height() / mRows_); + drawGrid(painter, cellSize); + drawShape(painter, cellSize); + drawNextShape(painter, cellSize); +} + +void TetrisGrid::drawGrid(QPainter &painter, int cellSize) { + for (int row = 0; row < mRows_; ++row) { + for (int column = 0; column < mColumns_; ++column) { + if (mGrid_[row][column] == 1) { + QColor color = generateRandomColor(); + painter.fillRect(column * cellSize, row * cellSize, cellSize, cellSize, color); + } else { + painter.fillRect(column * cellSize, row * cellSize, cellSize, cellSize, Qt::white); + painter.drawRect(column * cellSize, row * cellSize, cellSize, cellSize); + } + } + } +} + +void TetrisGrid::drawShape(QPainter &painter, int cellSize) { + for (int i = 0; i < currentShape_.size(); ++i) { + for (int j = 0; j < currentShape_[i].size(); ++j) { + if (currentShape_[i][j] == 1) { + QColor color = generateRandomColor(); + QBrush brush(color); + painter.setBrush(brush); + painter.fillRect((shapeColumn_ + j) * cellSize, (shapeRow_ + i) * cellSize, cellSize, cellSize, color); + painter.drawRect((shapeColumn_ + j) * cellSize, (shapeRow_ + i) * cellSize, cellSize, cellSize); + } + } + } +} + +void TetrisGrid::drawNextShape(QPainter &painter, int cellSize) { + for (int i = 0; i < nextShape_.size(); ++i) { + for (int j = 0; j < nextShape_[i].size(); ++j) { + if (nextShape_[i][j] == 1) { + QColor color = Qt::red; + QBrush brush(color); + painter.setBrush(brush); + painter.fillRect((4 + j) * cellSize, (i) * cellSize, cellSize, cellSize, color); + painter.drawRect((4 + j) * cellSize, (i) * cellSize, cellSize, cellSize); + } + } + } +} + +QColor TetrisGrid::generateRandomColor() { + return QColor(rand() % 256, rand() % 256, rand() % 256); +} + +void TetrisGrid::generateNewShape() { + shapeRow_ = 4; + shapeColumn_ = mColumns_ / 2 - 1; + int type = rand() % 6; + currentShape_.clear(); + currentShape_ = nextShape_; + nextShape_.clear(); + if (type == 0) { + nextShape_.append({1, 1}); + nextShape_.append({1, 1}); + } else if (type == 1) { + nextShape_.append({1, 1, 1, 1}); + } else if (type == 2){ + nextShape_.append({1, 1}); + nextShape_.append({1, 0}); + nextShape_.append({1, 0}); + } else if (type == 3){ + nextShape_.append({1, 1}); + } else if (type == 4){ + nextShape_.append({1, 1}); + nextShape_.append({0, 1}); + nextShape_.append({0, 1}); + } else { + nextShape_.append({0, 1, 0}); + nextShape_.append({1, 1, 1}); + } + if (currentShape_.size() == 0) { + generateNewShape(); + } + timer_->setInterval(speed_); +} + +void TetrisGrid::handleKeyPressEvent(QKeyEvent *event) { + keyPressEvent(event); +} + +void TetrisGrid::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Left: + moveShapeLeft(); + break; + case Qt::Key_Right: + moveShapeRight(); + break; + case Qt::Key_Up: + rotateShape(); + break; + case Qt::Key_Down: + speedFigure(); + break; + default: + QWidget::keyPressEvent(event); + } +} + +bool TetrisGrid::isCollision() { + for (int i = 0; i < currentShape_.size(); ++i) { + for (int j = 0; j < currentShape_[i].size(); ++j) { + if (currentShape_[i][j] == 1) { + int gridRow = shapeRow_ + i; + int gridColumn = shapeColumn_ + j; + if (gridRow < 0 || gridRow >= mRows_ || gridColumn < 0 || gridColumn >= mColumns_ || mGrid_[gridRow][gridColumn] == 1) { + return true; + } + } + } + } + return false; +} + +void TetrisGrid::checkLines() { + + for (int row = mRows_ - 1; row >= 0; --row) { + bool isLineFilled = true; + for (int column = 0; column < mColumns_; ++column) { + if (mGrid_[row][column] == 0) { + isLineFilled = false; + break; + } + } + if (isLineFilled) { + for (int r = row; r > 0; --r) { + mGrid_[r] = mGrid_[r - 1]; + } + mGrid_[0].fill(0); + ++score_; + updateScore(score_); + } + } +} diff --git a/tetrisgrid.h b/tetrisgrid.h new file mode 100644 index 0000000..b476393 --- /dev/null +++ b/tetrisgrid.h @@ -0,0 +1,63 @@ +#ifndef TETRISGRID_H +#define TETRISGRID_H + +#include +#include +#include + +class TetrisGrid : public QWidget { + Q_OBJECT + Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged) + Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged) +public: + int rows() const; + int columns() const; + bool isValidMove(const QVector> &shape, int rows, int columns); + void lockShape(); + void handleKeyPressEvent(QKeyEvent *event); + void setupGame(); + TetrisGrid(QWidget *parent = nullptr); + QColor generateRandomColor(); + +public slots: + void setRows(int rows); + void setColumns(int columns); + void startGame(); + void dropShape(); + void updateScore(int newScore); + void moveShapeLeft(); + void moveShapeRight(); + void rotateShape(); + void speedFigure(); + +signals: + void rowsChanged(int rows); + void columnsChanged(int columns); + void scoreChanged(int newScore); + void gameOver(); + +protected: + void paintEvent(QPaintEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + +private: + void drawGrid(QPainter &painter, int cellSize); + void drawShape(QPainter &painter, int cellSize); + void drawNextShape(QPainter &painter, int cellSize); + void generateNewShape(); + void checkLines(); + void addCurrentShapeToGridNew(); + bool isCollision(); + QVector> mGrid_; + QVector> currentShape_; + QVector> nextShape_; + QTimer *timer_; + int mRows_; + int mColumns_; + int shapeRow_; + int shapeColumn_; + int score_; + int speed_ = 500; +}; + +#endif // TETRISGRID_H